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.apm;
18  
19  import java.io.ByteArrayInputStream;
20  import java.io.CharArrayWriter;
21  import java.io.IOException;
22  import java.net.URISyntaxException;
23  import java.nio.charset.StandardCharsets;
24  import java.util.ArrayList;
25  
26  import org.hipparchus.geometry.euclidean.threed.RotationOrder;
27  import org.hipparchus.geometry.euclidean.threed.Vector3D;
28  import org.hipparchus.util.FastMath;
29  import org.junit.jupiter.api.Assertions;
30  import org.junit.jupiter.api.BeforeEach;
31  import org.junit.jupiter.api.Test;
32  import org.orekit.Utils;
33  import org.orekit.attitudes.Attitude;
34  import org.orekit.bodies.CelestialBodyFactory;
35  import org.orekit.data.DataContext;
36  import org.orekit.data.DataSource;
37  import org.orekit.errors.OrekitException;
38  import org.orekit.errors.OrekitMessages;
39  import org.orekit.files.ccsds.definitions.CelestialBodyFrame;
40  import org.orekit.files.ccsds.definitions.FrameFacade;
41  import org.orekit.files.ccsds.definitions.OrbitRelativeFrame;
42  import org.orekit.files.ccsds.definitions.SpacecraftBodyFrame;
43  import org.orekit.files.ccsds.definitions.SpacecraftBodyFrame.BaseEquipment;
44  import org.orekit.files.ccsds.ndm.ParserBuilder;
45  import org.orekit.files.ccsds.ndm.WriterBuilder;
46  import org.orekit.files.ccsds.ndm.adm.AdmMetadata;
47  import org.orekit.files.ccsds.section.Segment;
48  import org.orekit.files.ccsds.utils.generation.Generator;
49  import org.orekit.files.ccsds.utils.generation.KvnGenerator;
50  import org.orekit.frames.FramesFactory;
51  import org.orekit.time.AbsoluteDate;
52  import org.orekit.time.TimeOffset;
53  import org.orekit.time.TimeScalesFactory;
54  import org.orekit.utils.Constants;
55  import org.orekit.utils.IERSConventions;
56  import org.orekit.utils.PVCoordinates;
57  import org.orekit.utils.PVCoordinatesProvider;
58  import org.orekit.utils.TimeStampedPVCoordinates;
59  
60  public class APMParserTest {
61  
62      private static final double QUATERNION_PRECISION = 1e-5;
63      private static final double ANGLE_PRECISION = 1e-4;
64      private static final double SPACECRAFT_PRECISION = 0.1;
65      private static final double MANEUVER_PRECISION = 1.0e-2;
66  
67      @BeforeEach
68      public void setUp() {
69          Utils.setDataRoot("regular-data");
70      }
71  
72      @Test
73      public void testParseAPM1() {
74  
75          // File
76          final String ex = "/ccsds/adm/apm/APMExample01.txt";
77  
78          // Initialize the parser
79          final ApmParser parser = new ParserBuilder().
80                                   withMissionReferenceDate(new AbsoluteDate("2002-09-30T14:28:15.117",
81                                                                             TimeScalesFactory.getUTC())).
82                                   buildApmParser();
83  
84          final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
85  
86          // Generated APM file
87          final Apm file = parser.parseMessage(source);
88  
89          // Verify general data
90          Assertions.assertEquals(IERSConventions.IERS_2010, file.getConventions());
91          Assertions.assertEquals(DataContext.getDefault(),  file.getDataContext());
92  
93          // Check Header Block
94          Assertions.assertEquals(1.0, file.getHeader().getFormatVersion(), 1.0e-10);
95          Assertions.assertEquals(new AbsoluteDate(2003, 9, 30, 19, 23, 57,
96                                               TimeScalesFactory.getUTC()),
97                              file.getHeader().getCreationDate());
98          Assertions.assertEquals("GSFC", file.getHeader().getOriginator());
99  
100         // Check Metadata Block
101         Assertions.assertEquals("TRMM",       file.getMetadata().getObjectName());
102         Assertions.assertEquals("1997-009A",  file.getMetadata().getObjectID());
103         Assertions.assertEquals(1997,         file.getMetadata().getLaunchYear());
104         Assertions.assertEquals(9,            file.getMetadata().getLaunchNumber());
105         Assertions.assertEquals("A",          file.getMetadata().getLaunchPiece());
106         Assertions.assertEquals("EARTH",      file.getMetadata().getCenter().getName());
107         Assertions.assertTrue(file.getMetadata().getHasCreatableBody());
108         Assertions.assertEquals(CelestialBodyFactory.getEarth(), file.getMetadata().getCenter().getBody());
109         Assertions.assertEquals("UTC",        file.getMetadata().getTimeSystem().name());
110 
111         // Check data block
112         Assertions.assertFalse(file.getData().hasManeuvers());
113         Assertions.assertEquals(SpacecraftBodyFrame.BaseEquipment.SC_BODY,
114                             file.getData().getQuaternionBlock().getEndpoints().getFrameA().asSpacecraftBodyFrame().getBaseEquipment());
115         Assertions.assertEquals("1", file.getData().getQuaternionBlock().getEndpoints().getFrameA().asSpacecraftBodyFrame().getLabel());
116         Assertions.assertEquals(CelestialBodyFrame.ITRF1997, file.getData().getQuaternionBlock().getEndpoints().getFrameB().asCelestialBodyFrame());
117         Assertions.assertTrue(file.getData().getQuaternionBlock().getEndpoints().isA2b());
118         Assertions.assertEquals(new AbsoluteDate(2003, 9, 30, 14, 28, new TimeOffset(15, TimeOffset.SECOND, 117200, TimeOffset.MICROSECOND),
119                                              TimeScalesFactory.getUTC()),
120                             file.getData().getEpoch());
121         Assertions.assertEquals(0.25678, file.getData().getQuaternionBlock().getQuaternion().getQ0(),    QUATERNION_PRECISION);
122         Assertions.assertEquals(0.00005, file.getData().getQuaternionBlock().getQuaternion().getQ1(),    QUATERNION_PRECISION);
123         Assertions.assertEquals(0.87543, file.getData().getQuaternionBlock().getQuaternion().getQ2(),    QUATERNION_PRECISION);
124         Assertions.assertEquals(0.40949, file.getData().getQuaternionBlock().getQuaternion().getQ3(),    QUATERNION_PRECISION);
125         Assertions.assertFalse(file.getData().getQuaternionBlock().hasRates());
126         Assertions.assertTrue(Double.isNaN(file.getData().getQuaternionBlock().getQuaternionDot().getQ1()));
127         Assertions.assertEquals(new AbsoluteDate(2003, 9, 30, 14, 28, new TimeOffset(15, TimeOffset.SECOND, 117200, TimeOffset.MICROSECOND),
128                                              TimeScalesFactory.getUTC()),
129                             file.getAttitude(null, null).getDate());
130         Assertions.assertEquals(0.0, file.getAttitude(null, null).getSpin().getNorm(), 1.0e-15);
131 
132         Attitude attitude = file.getAttitude(null, null);
133         Assertions.assertEquals(new AbsoluteDate(2003, 9, 30, 14, 28, new TimeOffset(15, TimeOffset.SECOND, 117200, TimeOffset.MICROSECOND), TimeScalesFactory.getUTC()),
134                             attitude.getDate());
135         Assertions.assertEquals("ITRF-1997/CIO/2010-based ITRF simple EOP", attitude.getReferenceFrame().getName());
136         Assertions.assertEquals(2 * FastMath.atan(FastMath.sqrt(0.00005 * 0.00005 + 0.87543 * 0.87543 + 0.40949 * 0.40949) / 0.25678),
137                             attitude.getRotation().getAngle(), 1.0e-15);
138         Assertions.assertEquals(0, attitude.getSpin().getNorm(), 1.0e-15);
139 
140     }
141 
142     @Test
143     public void testParseAPM2KVN() {
144         final ApmParser parser = new ParserBuilder().
145                                  withMissionReferenceDate(new AbsoluteDate("2002-09-30T14:28:15.117",
146                                                                            TimeScalesFactory.getUTC())).
147                                  buildApmParser();
148         final String name = "/ccsds/adm/apm/APMExample02.txt";
149         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
150         validateAPM2(parser.parseMessage(source));
151     }
152 
153     @Test
154     public void testParseAPM2XML() {
155         final ApmParser parser = new ParserBuilder().
156                         withMissionReferenceDate(new AbsoluteDate("2002-09-30T14:28:15.117",
157                                                                   TimeScalesFactory.getUTC())).
158                         buildApmParser();
159         final String name = "/ccsds/adm/apm/APMExample02.xml";
160         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
161         validateAPM2(parser.parseMessage(source));
162     }
163 
164     @Test
165     public void testWriteApm2() throws IOException {
166         final ApmParser parser = new ParserBuilder().
167                         withMissionReferenceDate(new AbsoluteDate("2002-09-30T14:28:15.117",
168                                                                   TimeScalesFactory.getUTC())).
169                         buildApmParser();
170         final String name = "/ccsds/adm/apm/APMExample02.xml";
171         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
172         final Apm original = parser.parseMessage(source);
173 
174         // write the parsed file back to a characters array
175         final CharArrayWriter caw = new CharArrayWriter();
176         final Generator generator = new KvnGenerator(caw, ApmWriter.KVN_PADDING_WIDTH, "dummy",
177                                                      Constants.JULIAN_DAY, 60);
178         new WriterBuilder().buildApmWriter().writeMessage(generator, original);
179 
180         // reparse the written file
181         final byte[]     bytes   = caw.toString().getBytes(StandardCharsets.UTF_8);
182         final DataSource source2 = new DataSource(name, () -> new ByteArrayInputStream(bytes));
183         final Apm    rebuilt = new ParserBuilder().buildApmParser().parseMessage(source2);
184         validateAPM2(rebuilt);
185     }
186 
187     private void validateAPM2(final Apm file) {
188 
189         // Verify general data
190         Assertions.assertEquals(IERSConventions.IERS_2010, file.getConventions());
191         Assertions.assertEquals(DataContext.getDefault(),  file.getDataContext());
192 
193         // Check Header Block
194         Assertions.assertEquals(1.0, file.getHeader().getFormatVersion(), 1.0e-10);
195         Assertions.assertEquals(new AbsoluteDate(2004, 2, 14, 19, 23, 57,
196                                              TimeScalesFactory.getUTC()),
197                             file.getHeader().getCreationDate());
198         Assertions.assertEquals("JPL", file.getHeader().getOriginator());
199 
200         Segment<AdmMetadata, ApmData> segment = file.getSegments().get(0);
201 
202         // Check Metadata Block
203         Assertions.assertEquals("MARS SPIRIT", segment.getMetadata().getObjectName());
204         Assertions.assertEquals("2004-003A",   segment.getMetadata().getObjectID());
205         Assertions.assertEquals(2004,          segment.getMetadata().getLaunchYear());
206         Assertions.assertEquals(3,             segment.getMetadata().getLaunchNumber());
207         Assertions.assertEquals("A",           segment.getMetadata().getLaunchPiece());
208         Assertions.assertEquals("EARTH",       segment.getMetadata().getCenter().getName());
209         Assertions.assertTrue(segment.getMetadata().getHasCreatableBody());
210         Assertions.assertTrue(segment.getMetadata().getComments().isEmpty());
211         Assertions.assertEquals(CelestialBodyFactory.getEarth(), segment.getMetadata().getCenter().getBody());
212         Assertions.assertEquals("UTC",         segment.getMetadata().getTimeSystem().name());
213 
214         // Check general comments
215         ArrayList<String> generalComment = new ArrayList<>();
216         generalComment.add("GEOCENTRIC, CARTESIAN, EARTH FIXED");
217         generalComment.add("OBJECT ID: 2004-003");
218         generalComment.add("$ITIM = 2004 JAN 14 22:26:18.400000, original launch time 14:36");
219         generalComment.add("Generated by JPL");
220         generalComment.add("Current attitude for orbit 20 and attitude maneuver");
221         generalComment.add("planning data.");
222         generalComment.add("Attitude state quaternion");
223         Assertions.assertEquals(generalComment, segment.getData().getComments());
224 
225         // Check data block: QUATERNION
226         Assertions.assertEquals(SpacecraftBodyFrame.BaseEquipment.INSTRUMENT,
227                             segment.getData().getQuaternionBlock().getEndpoints().getFrameA().asSpacecraftBodyFrame().getBaseEquipment());
228         Assertions.assertEquals("A", segment.getData().getQuaternionBlock().getEndpoints().getFrameA().asSpacecraftBodyFrame().getLabel());
229         Assertions.assertEquals(CelestialBodyFrame.ITRF1997, segment.getData().getQuaternionBlock().getEndpoints().getFrameB().asCelestialBodyFrame());
230         Assertions.assertEquals(new AbsoluteDate(2004, 2, 14, 14, 28, new TimeOffset(15, TimeOffset.SECOND, 117200, TimeOffset.MICROSECOND),
231                                              TimeScalesFactory.getUTC()),
232                             segment.getData().getEpoch());
233 
234         Assertions.assertEquals(0.47832, segment.getData().getQuaternionBlock().getQuaternion().getQ0(),    QUATERNION_PRECISION);
235         Assertions.assertEquals(0.03123, segment.getData().getQuaternionBlock().getQuaternion().getQ1(),    QUATERNION_PRECISION);
236         Assertions.assertEquals(0.78543, segment.getData().getQuaternionBlock().getQuaternion().getQ2(),    QUATERNION_PRECISION);
237         Assertions.assertEquals(0.39158, segment.getData().getQuaternionBlock().getQuaternion().getQ3(),    QUATERNION_PRECISION);
238         Assertions.assertFalse(segment.getData().getQuaternionBlock().hasRates());
239         Assertions.assertTrue(Double.isNaN(segment.getData().getQuaternionBlock().getQuaternionDot().getQ1()));
240 
241         // Check data block: EULER
242         ArrayList<String> eulerComment = new ArrayList<>();
243         eulerComment.add("Attitude specified as Euler elements");
244         Assertions.assertEquals(eulerComment,    segment.getData().getEulerBlock().getComments());
245         Assertions.assertEquals(CelestialBodyFrame.ITRF1997, segment.getData().getEulerBlock().getEndpoints().getFrameA().asCelestialBodyFrame());
246         Assertions.assertEquals(SpacecraftBodyFrame.BaseEquipment.INSTRUMENT,  segment.getData().getEulerBlock().getEndpoints().getFrameB().asSpacecraftBodyFrame().getBaseEquipment());
247         Assertions.assertEquals("A",  segment.getData().getEulerBlock().getEndpoints().getFrameB().asSpacecraftBodyFrame().getLabel());
248         Assertions.assertTrue(segment.getData().getEulerBlock().getEndpoints().isA2b());
249         Assertions.assertTrue(segment.getData().getEulerBlock().rateFrameIsA());
250         Assertions.assertEquals(RotationOrder.ZXY, segment.getData().getEulerBlock().getEulerRotSeq());
251 
252         Assertions.assertEquals(-53.3688,  FastMath.toDegrees(segment.getData().getEulerBlock().getRotationAngles()[0]), ANGLE_PRECISION);
253         Assertions.assertEquals(139.7527,  FastMath.toDegrees(segment.getData().getEulerBlock().getRotationAngles()[1]), ANGLE_PRECISION);
254         Assertions.assertEquals( 25.0658,  FastMath.toDegrees(segment.getData().getEulerBlock().getRotationAngles()[2]), ANGLE_PRECISION);
255         Assertions.assertEquals(  0.02156, FastMath.toDegrees(segment.getData().getEulerBlock().getRotationRates()[0]),  ANGLE_PRECISION);
256         Assertions.assertEquals(  0.1045,  FastMath.toDegrees(segment.getData().getEulerBlock().getRotationRates()[1]),  ANGLE_PRECISION);
257         Assertions.assertEquals(  0.03214, FastMath.toDegrees(segment.getData().getEulerBlock().getRotationRates()[2]),  ANGLE_PRECISION);
258 
259         // Check data block: SPACECRAFT PARAMETERS
260         ArrayList<String> spacecraftComment = new ArrayList<>();
261         spacecraftComment.add("Spacecraft Parameters");
262         Assertions.assertEquals(spacecraftComment, segment.getData().getInertiaBlock().getComments());
263         Assertions.assertEquals(6080.0,            segment.getData().getInertiaBlock().getInertiaMatrix().getEntry(0, 0), SPACECRAFT_PRECISION);
264         Assertions.assertEquals(5245.5,            segment.getData().getInertiaBlock().getInertiaMatrix().getEntry(1, 1), SPACECRAFT_PRECISION);
265         Assertions.assertEquals(8067.3,            segment.getData().getInertiaBlock().getInertiaMatrix().getEntry(2, 2), SPACECRAFT_PRECISION);
266         Assertions.assertEquals(-135.9,            segment.getData().getInertiaBlock().getInertiaMatrix().getEntry(0, 1), SPACECRAFT_PRECISION);
267         Assertions.assertEquals(89.3,              segment.getData().getInertiaBlock().getInertiaMatrix().getEntry(0, 2), SPACECRAFT_PRECISION);
268         Assertions.assertEquals(-90.7,             segment.getData().getInertiaBlock().getInertiaMatrix().getEntry(1, 2), SPACECRAFT_PRECISION);
269 
270         // Check data block: MANEUVER
271         ArrayList<String> maneuverComment = new ArrayList<>();
272         maneuverComment.add("Data follows for 1 planned maneuver.");
273         maneuverComment.add("First attitude maneuver for: MARS SPIRIT");
274         maneuverComment.add("Impulsive, torque direction fixed in body frame");
275         Assertions.assertEquals(maneuverComment, segment.getData().getManeuver(0).getComments());
276         Assertions.assertTrue(segment.getData().hasManeuvers());
277         Assertions.assertEquals(1, segment.getData().getNbManeuvers());
278         Assertions.assertEquals(1, segment.getData().getManeuvers().size());
279         Assertions.assertEquals(BaseEquipment.INSTRUMENT, segment.getData().getManeuver(0).getFrame().asSpacecraftBodyFrame().getBaseEquipment());
280         Assertions.assertEquals("A", segment.getData().getManeuver(0).getFrame().asSpacecraftBodyFrame().getLabel());
281         Assertions.assertEquals(new AbsoluteDate(2004, 2, 14, 14, 29, 0.5098,
282                                              TimeScalesFactory.getUTC()),
283                             segment.getData().getManeuver(0).getEpochStart());
284         Assertions.assertEquals(3,     segment.getData().getManeuver(0).getDuration(),      MANEUVER_PRECISION);
285         Assertions.assertEquals(-1.25, segment.getData().getManeuver(0).getTorque().getX(), MANEUVER_PRECISION);
286         Assertions.assertEquals(-0.5,  segment.getData().getManeuver(0).getTorque().getY(), MANEUVER_PRECISION);
287         Assertions.assertEquals(0.5,   segment.getData().getManeuver(0).getTorque().getZ(), MANEUVER_PRECISION);
288 
289     }
290 
291     @Test
292     public void testParseAPM3() {
293 
294         // File
295         final String ex = "/ccsds/adm/apm/APMExample03.txt";
296 
297         // Initialize the parser
298         final ApmParser parser = new ParserBuilder().
299                                  withMissionReferenceDate(new AbsoluteDate("2002-09-30T14:28:15.117",
300                                                                            TimeScalesFactory.getUTC())).
301                                  buildApmParser();
302 
303         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
304 
305         // Generated APM file
306         final Apm file = parser.parseMessage(source);
307 
308         // Verify general data
309         Assertions.assertEquals(IERSConventions.IERS_2010, file.getConventions());
310         Assertions.assertEquals(DataContext.getDefault(),  file.getDataContext());
311 
312         // Check Header Block
313         Assertions.assertEquals(1.0, file.getHeader().getFormatVersion(), 1.0e-10);
314         Assertions.assertEquals(new AbsoluteDate(2003, 9, 30, 19, 23, 57,
315                                              TimeScalesFactory.getUTC()),
316                             file.getHeader().getCreationDate());
317         Assertions.assertEquals("GSFC", file.getHeader().getOriginator());
318 
319         Segment<AdmMetadata, ApmData> segment = file.getSegments().get(0);
320 
321         // Check Metadata Block
322         Assertions.assertEquals("TRMM",       segment.getMetadata().getObjectName());
323         Assertions.assertEquals("1997-009A",  segment.getMetadata().getObjectID());
324         Assertions.assertEquals(1997,         segment.getMetadata().getLaunchYear());
325         Assertions.assertEquals(9,            segment.getMetadata().getLaunchNumber());
326         Assertions.assertEquals("A",          segment.getMetadata().getLaunchPiece());
327         Assertions.assertEquals("EARTH",      segment.getMetadata().getCenter().getName());
328         Assertions.assertTrue(segment.getMetadata().getHasCreatableBody());
329         Assertions.assertEquals(CelestialBodyFactory.getEarth(), segment.getMetadata().getCenter().getBody());
330         Assertions.assertEquals("UTC",        segment.getMetadata().getTimeSystem().name());
331 
332         // Check data block: QUATERNION
333         Assertions.assertFalse(segment.getData().hasManeuvers());
334         Assertions.assertEquals(SpacecraftBodyFrame.BaseEquipment.SC_BODY,
335                             segment.getData().getQuaternionBlock().getEndpoints().getFrameA().asSpacecraftBodyFrame().getBaseEquipment());
336         Assertions.assertEquals("1", segment.getData().getQuaternionBlock().getEndpoints().getFrameA().asSpacecraftBodyFrame().getLabel());
337         Assertions.assertEquals(CelestialBodyFrame.ITRF1997, segment.getData().getQuaternionBlock().getEndpoints().getFrameB().asCelestialBodyFrame());
338         Assertions.assertFalse(segment.getData().getQuaternionBlock().getEndpoints().isA2b());
339         Assertions.assertEquals(new AbsoluteDate(2003, 9, 30, 14, 28, new TimeOffset(15, TimeOffset.SECOND, 117200, TimeOffset.MICROSECOND),
340                                              TimeScalesFactory.getUTC()),
341                             segment.getData().getEpoch());
342         Assertions.assertEquals(0.25678, segment.getData().getQuaternionBlock().getQuaternion().getQ0(),    QUATERNION_PRECISION);
343         Assertions.assertEquals(0.00005, segment.getData().getQuaternionBlock().getQuaternion().getQ1(),    QUATERNION_PRECISION);
344         Assertions.assertEquals(0.87543, segment.getData().getQuaternionBlock().getQuaternion().getQ2(),    QUATERNION_PRECISION);
345         Assertions.assertEquals(0.40949, segment.getData().getQuaternionBlock().getQuaternion().getQ3(),    QUATERNION_PRECISION);
346         Assertions.assertFalse(segment.getData().getQuaternionBlock().hasRates());
347         Assertions.assertTrue(Double.isNaN(segment.getData().getQuaternionBlock().getQuaternionDot().getQ1()));
348 
349         // Check data block: SPIN
350         ArrayList<String> spinComment = new ArrayList<>();
351         spinComment.add("SPIN Parameters");
352         Assertions.assertEquals(spinComment, segment.getData().getSpinStabilizedBlock().getComments());
353         Assertions.assertEquals(SpacecraftBodyFrame.BaseEquipment.SC_BODY,
354                             segment.getData().getSpinStabilizedBlock().getEndpoints().getFrameB().asSpacecraftBodyFrame().getBaseEquipment());
355         Assertions.assertEquals("1", segment.getData().getSpinStabilizedBlock().getEndpoints().getFrameB().asSpacecraftBodyFrame().getLabel());
356         Assertions.assertEquals(CelestialBodyFrame.ITRF1997, segment.getData().getSpinStabilizedBlock().getEndpoints().getFrameA().asCelestialBodyFrame());
357         Assertions.assertFalse(segment.getData().getSpinStabilizedBlock().getEndpoints().isA2b());
358         Assertions.assertEquals(FastMath.toRadians(24.8),   segment.getData().getSpinStabilizedBlock().getSpinAlpha(),      ANGLE_PRECISION);
359         Assertions.assertEquals(FastMath.toRadians(33.7),   segment.getData().getSpinStabilizedBlock().getSpinDelta(),      ANGLE_PRECISION);
360         Assertions.assertEquals(FastMath.toRadians(42.5),   segment.getData().getSpinStabilizedBlock().getSpinAngle(),      ANGLE_PRECISION);
361         Assertions.assertEquals(FastMath.toRadians(-135.9), segment.getData().getSpinStabilizedBlock().getSpinAngleVel(),   ANGLE_PRECISION);
362         Assertions.assertEquals(FastMath.toRadians(89.3),   segment.getData().getSpinStabilizedBlock().getNutation(),       ANGLE_PRECISION);
363         Assertions.assertEquals(FastMath.toRadians(-90.7),  segment.getData().getSpinStabilizedBlock().getNutationPhase(),  ANGLE_PRECISION);
364         Assertions.assertEquals(64.0,                       segment.getData().getSpinStabilizedBlock().getNutationPeriod(), ANGLE_PRECISION);
365 
366     }
367 
368     @Test
369     public void testParseAPM4() {
370 
371         // File
372         final String ex = "/ccsds/adm/apm/APMExample04.txt";
373 
374         // Initialize the parser
375         final ApmParser parser = new ParserBuilder().
376                                  withMissionReferenceDate(new AbsoluteDate("2002-09-30T14:28:15.117",
377                                                                            TimeScalesFactory.getUTC())).
378                                  buildApmParser();
379 
380         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
381 
382         // Generated APM file
383         final Apm file = parser.parseMessage(source);
384 
385         // Verify general data
386         Assertions.assertEquals(IERSConventions.IERS_2010, file.getConventions());
387         Assertions.assertEquals(DataContext.getDefault(),  file.getDataContext());
388 
389         // Check Header Block
390         Assertions.assertEquals(1.0, file.getHeader().getFormatVersion(), 1.0e-10);
391         Assertions.assertEquals(new AbsoluteDate(2003, 9, 30, 19, 23, 57,
392                                              TimeScalesFactory.getUTC()),
393                             file.getHeader().getCreationDate());
394         Assertions.assertEquals("GSFC", file.getHeader().getOriginator());
395 
396         Segment<AdmMetadata, ApmData> segment = file.getSegments().get(0);
397 
398         // Check Metadata Block
399         Assertions.assertEquals("TRMM",       segment.getMetadata().getObjectName());
400         Assertions.assertEquals("1997-009A",  segment.getMetadata().getObjectID());
401         Assertions.assertEquals(1997,         segment.getMetadata().getLaunchYear());
402         Assertions.assertEquals(9,            segment.getMetadata().getLaunchNumber());
403         Assertions.assertEquals("A",          segment.getMetadata().getLaunchPiece());
404         Assertions.assertEquals("EARTH",      segment.getMetadata().getCenter().getName());
405         Assertions.assertTrue(segment.getMetadata().getHasCreatableBody());
406         Assertions.assertEquals(CelestialBodyFactory.getEarth(), segment.getMetadata().getCenter().getBody());
407         Assertions.assertEquals("UTC",        segment.getMetadata().getTimeSystem().name());
408 
409         // Check data block
410         Assertions.assertFalse(segment.getData().hasManeuvers());
411         Assertions.assertEquals(SpacecraftBodyFrame.BaseEquipment.SC_BODY,
412                             segment.getData().getQuaternionBlock().getEndpoints().getFrameA().asSpacecraftBodyFrame().getBaseEquipment());
413         Assertions.assertEquals("1", segment.getData().getQuaternionBlock().getEndpoints().getFrameA().asSpacecraftBodyFrame().getLabel());
414         Assertions.assertEquals(CelestialBodyFrame.ITRF1997, segment.getData().getQuaternionBlock().getEndpoints().getFrameB().asCelestialBodyFrame());
415         Assertions.assertTrue(segment.getData().getQuaternionBlock().getEndpoints().isA2b());
416         Assertions.assertEquals(new AbsoluteDate(2003, 9, 30, 14, 28, new TimeOffset(15, TimeOffset.SECOND, 117200, TimeOffset.MICROSECOND),
417                                              TimeScalesFactory.getUTC()),
418                             segment.getData().getEpoch());
419         Assertions.assertEquals(0.25678, segment.getData().getQuaternionBlock().getQuaternion().getQ0(),    QUATERNION_PRECISION);
420         Assertions.assertEquals(0.00005, segment.getData().getQuaternionBlock().getQuaternion().getQ1(),    QUATERNION_PRECISION);
421         Assertions.assertEquals(0.87543, segment.getData().getQuaternionBlock().getQuaternion().getQ2(),    QUATERNION_PRECISION);
422         Assertions.assertEquals(0.40949, segment.getData().getQuaternionBlock().getQuaternion().getQ3(),    QUATERNION_PRECISION);
423         Assertions.assertEquals(0.05678, segment.getData().getQuaternionBlock().getQuaternionDot().getQ0(), QUATERNION_PRECISION);
424         Assertions.assertEquals(0.00001, segment.getData().getQuaternionBlock().getQuaternionDot().getQ1(), QUATERNION_PRECISION);
425         Assertions.assertEquals(0.07543, segment.getData().getQuaternionBlock().getQuaternionDot().getQ2(), QUATERNION_PRECISION);
426         Assertions.assertEquals(0.00949, segment.getData().getQuaternionBlock().getQuaternionDot().getQ3(), QUATERNION_PRECISION);
427         Assertions.assertEquals(new AbsoluteDate(2003, 9, 30, 14, 28, new TimeOffset(15, TimeOffset.SECOND, 117200, TimeOffset.MICROSECOND), TimeScalesFactory.getUTC()),
428                                 segment.getData().getAttitude(null, null).getDate());
429         Assertions.assertEquals(8.63363e-2, segment.getData().getAttitude(null, null).getSpin().getNorm(), 1.0e-7);
430 
431         Attitude attitude = file.getAttitude(null, null);
432         Assertions.assertEquals(segment.getData().getEpoch(), attitude.getDate());
433         Assertions.assertEquals(8.63363e-2, attitude.getSpin().getNorm(), 1.0e-7);
434 
435     }
436 
437     @Test
438     public void testParseAPM5() {
439 
440         // File
441         final String ex = "/ccsds/adm/apm/APMExample05.txt";
442 
443         // Initialize the parser
444         final ApmParser parser = new ParserBuilder().buildApmParser();
445 
446         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
447 
448         // Generated APM file
449         final Apm file = parser.parseMessage(source);
450 
451         // Verify general data
452         Assertions.assertEquals(IERSConventions.IERS_2010, file.getConventions());
453         Assertions.assertEquals(DataContext.getDefault(),  file.getDataContext());
454 
455         // Check Header Block
456         Assertions.assertEquals(1.0, file.getHeader().getFormatVersion(), 1.0e-10);
457         Assertions.assertEquals(new AbsoluteDate(2004, 2, 14, 19, 23, 57,
458                                              TimeScalesFactory.getUTC()),
459                             file.getHeader().getCreationDate());
460         Assertions.assertEquals("GSFC", file.getHeader().getOriginator());
461 
462         Segment<AdmMetadata, ApmData> segment = file.getSegments().get(0);
463 
464         // Check Metadata Block
465         Assertions.assertEquals("TRMM",       segment.getMetadata().getObjectName());
466         Assertions.assertEquals("1997-009A",  segment.getMetadata().getObjectID());
467         Assertions.assertEquals(1997,         segment.getMetadata().getLaunchYear());
468         Assertions.assertEquals(9,            segment.getMetadata().getLaunchNumber());
469         Assertions.assertEquals("A",          segment.getMetadata().getLaunchPiece());
470         Assertions.assertEquals("EARTH",      segment.getMetadata().getCenter().getName());
471         Assertions.assertTrue(segment.getMetadata().getHasCreatableBody());
472         Assertions.assertEquals(CelestialBodyFactory.getEarth(), segment.getMetadata().getCenter().getBody());
473         Assertions.assertEquals("UTC",        segment.getMetadata().getTimeSystem().name());
474 
475         // Check data block
476         Assertions.assertFalse(segment.getData().hasManeuvers());
477         Assertions.assertEquals(SpacecraftBodyFrame.BaseEquipment.SC_BODY,
478                             segment.getData().getQuaternionBlock().getEndpoints().getFrameA().asSpacecraftBodyFrame().getBaseEquipment());
479         Assertions.assertEquals("1", segment.getData().getQuaternionBlock().getEndpoints().getFrameA().asSpacecraftBodyFrame().getLabel());
480         Assertions.assertEquals(CelestialBodyFrame.ITRF1997, segment.getData().getQuaternionBlock().getEndpoints().getFrameB().asCelestialBodyFrame());
481         Assertions.assertTrue(segment.getData().getQuaternionBlock().getEndpoints().isA2b());
482         Assertions.assertEquals(new AbsoluteDate(2004, 2, 14, 14, 28, new TimeOffset(15, TimeOffset.SECOND, 117200, TimeOffset.MICROSECOND),
483                                              TimeScalesFactory.getUTC()),
484                             segment.getData().getEpoch());
485         Assertions.assertEquals(0.47832, segment.getData().getQuaternionBlock().getQuaternion().getQ0(), QUATERNION_PRECISION);
486         Assertions.assertEquals(0.03123, segment.getData().getQuaternionBlock().getQuaternion().getQ1(), QUATERNION_PRECISION);
487         Assertions.assertEquals(0.78543, segment.getData().getQuaternionBlock().getQuaternion().getQ2(), QUATERNION_PRECISION);
488         Assertions.assertEquals(0.39158, segment.getData().getQuaternionBlock().getQuaternion().getQ3(), QUATERNION_PRECISION);
489         Assertions.assertTrue(Double.isNaN(segment.getData().getQuaternionBlock().getQuaternionDot().getQ0()));
490         Assertions.assertTrue(Double.isNaN(segment.getData().getQuaternionBlock().getQuaternionDot().getQ1()));
491         Assertions.assertTrue(Double.isNaN(segment.getData().getQuaternionBlock().getQuaternionDot().getQ2()));
492         Assertions.assertTrue(Double.isNaN(segment.getData().getQuaternionBlock().getQuaternionDot().getQ3()));
493 
494         Assertions.assertEquals(RotationOrder.ZXY, segment.getData().getEulerBlock().getEulerRotSeq());
495         Assertions.assertFalse(segment.getData().getEulerBlock().hasAngles());
496         Assertions.assertTrue(Double.isNaN(segment.getData().getEulerBlock().getRotationAngles()[0]));
497         Assertions.assertTrue(Double.isNaN(segment.getData().getEulerBlock().getRotationAngles()[1]));
498         Assertions.assertTrue(Double.isNaN(segment.getData().getEulerBlock().getRotationAngles()[2]));
499         Assertions.assertEquals(0.02156, FastMath.toDegrees(segment.getData().getEulerBlock().getRotationRates()[0]), ANGLE_PRECISION);
500         Assertions.assertEquals(0.1045,  FastMath.toDegrees(segment.getData().getEulerBlock().getRotationRates()[1]), ANGLE_PRECISION);
501         Assertions.assertEquals(0.03214, FastMath.toDegrees(segment.getData().getEulerBlock().getRotationRates()[2]), ANGLE_PRECISION);
502 
503         Attitude attitude = file.getAttitude(null, null);
504         Assertions.assertEquals(segment.getData().getEpoch(), attitude.getDate());
505         Assertions.assertEquals(2.0137e-3, attitude.getSpin().getNorm(), 1.0e-7);
506 
507     }
508 
509     @Test
510     public void testParseAPM6() {
511 
512         // File
513         final String ex = "/ccsds/adm/apm/APMExample06.txt";
514 
515         // Initialize the parser
516         final ApmParser parser = new ParserBuilder().buildApmParser();
517 
518         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
519 
520         // Generated APM file
521         final Apm file = parser.parseMessage(source);
522 
523         // Verify general data
524         Assertions.assertEquals(IERSConventions.IERS_2010, file.getConventions());
525         Assertions.assertEquals(DataContext.getDefault(),  file.getDataContext());
526 
527         // Check Header Block
528         Assertions.assertEquals(1.0, file.getHeader().getFormatVersion(), 1.0e-10);
529         Assertions.assertEquals(new AbsoluteDate(2004, 2, 14, 19, 23, 57,
530                                              TimeScalesFactory.getUTC()),
531                             file.getHeader().getCreationDate());
532         Assertions.assertEquals("GSFC", file.getHeader().getOriginator());
533 
534         Segment<AdmMetadata, ApmData> segment = file.getSegments().get(0);
535 
536         // Check Metadata Block
537         Assertions.assertEquals("TRMM",       segment.getMetadata().getObjectName());
538         Assertions.assertEquals("1997-009A",  segment.getMetadata().getObjectID());
539         Assertions.assertEquals(1997,         segment.getMetadata().getLaunchYear());
540         Assertions.assertEquals(9,            segment.getMetadata().getLaunchNumber());
541         Assertions.assertEquals("A",          segment.getMetadata().getLaunchPiece());
542         Assertions.assertEquals("EARTH",      segment.getMetadata().getCenter().getName());
543         Assertions.assertTrue(segment.getMetadata().getHasCreatableBody());
544         Assertions.assertEquals(CelestialBodyFactory.getEarth(), segment.getMetadata().getCenter().getBody());
545         Assertions.assertEquals("UTC",        segment.getMetadata().getTimeSystem().name());
546 
547         // Check data block
548         Assertions.assertFalse(segment.getData().hasManeuvers());
549         Assertions.assertEquals(SpacecraftBodyFrame.BaseEquipment.SC_BODY,
550                             segment.getData().getQuaternionBlock().getEndpoints().getFrameA().asSpacecraftBodyFrame().getBaseEquipment());
551         Assertions.assertEquals("1", segment.getData().getQuaternionBlock().getEndpoints().getFrameA().asSpacecraftBodyFrame().getLabel());
552         Assertions.assertEquals(CelestialBodyFrame.ITRF1997, segment.getData().getQuaternionBlock().getEndpoints().getFrameB().asCelestialBodyFrame());
553         Assertions.assertTrue(segment.getData().getQuaternionBlock().getEndpoints().isA2b());
554         Assertions.assertEquals(new AbsoluteDate(2004, 2, 14, 14, 28, new TimeOffset(15, TimeOffset.SECOND, 117200, TimeOffset.MICROSECOND),
555                                              TimeScalesFactory.getUTC()),
556                             segment.getData().getEpoch());
557         Assertions.assertEquals(0.47832, segment.getData().getQuaternionBlock().getQuaternion().getQ0(), QUATERNION_PRECISION);
558         Assertions.assertEquals(0.03123, segment.getData().getQuaternionBlock().getQuaternion().getQ1(), QUATERNION_PRECISION);
559         Assertions.assertEquals(0.78543, segment.getData().getQuaternionBlock().getQuaternion().getQ2(), QUATERNION_PRECISION);
560         Assertions.assertEquals(0.39158, segment.getData().getQuaternionBlock().getQuaternion().getQ3(), QUATERNION_PRECISION);
561         Assertions.assertTrue(Double.isNaN(segment.getData().getQuaternionBlock().getQuaternionDot().getQ0()));
562         Assertions.assertTrue(Double.isNaN(segment.getData().getQuaternionBlock().getQuaternionDot().getQ1()));
563         Assertions.assertTrue(Double.isNaN(segment.getData().getQuaternionBlock().getQuaternionDot().getQ2()));
564         Assertions.assertTrue(Double.isNaN(segment.getData().getQuaternionBlock().getQuaternionDot().getQ3()));
565 
566         Assertions.assertEquals(RotationOrder.ZXY, segment.getData().getEulerBlock().getEulerRotSeq());
567         Assertions.assertEquals(0.02156, FastMath.toDegrees(segment.getData().getEulerBlock().getRotationAngles()[0]), ANGLE_PRECISION);
568         Assertions.assertEquals(0.1045,  FastMath.toDegrees(segment.getData().getEulerBlock().getRotationAngles()[1]), ANGLE_PRECISION);
569         Assertions.assertEquals(0.03214, FastMath.toDegrees(segment.getData().getEulerBlock().getRotationAngles()[2]), ANGLE_PRECISION);
570         Assertions.assertTrue(Double.isNaN(segment.getData().getEulerBlock().getRotationRates()[0]));
571         Assertions.assertTrue(Double.isNaN(segment.getData().getEulerBlock().getRotationRates()[1]));
572         Assertions.assertTrue(Double.isNaN(segment.getData().getEulerBlock().getRotationRates()[2]));
573 
574         Attitude attitude = file.getAttitude(null, null);
575         Assertions.assertEquals(segment.getData().getEpoch(),
576                             attitude.getDate());
577         Assertions.assertEquals(0.0, attitude.getSpin().getNorm(), 1.0e-15);
578 
579     }
580 
581     @Test
582     public void testParseAPM7() {
583 
584         // File
585         final String ex = "/ccsds/adm/apm/APMExample07.txt";
586 
587         // Initialize the parser
588         final ApmParser parser = new ParserBuilder().buildApmParser();
589 
590         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
591 
592         // Generated APM file
593         final Apm file = parser.parseMessage(source);
594  
595         // Verify general data
596         Assertions.assertEquals(IERSConventions.IERS_2010, file.getConventions());
597         Assertions.assertEquals(DataContext.getDefault(),  file.getDataContext());
598 
599         // Check Header Block
600         Assertions.assertEquals(2.0, file.getHeader().getFormatVersion(), 1.0e-10);
601         Assertions.assertEquals(new AbsoluteDate(2023, 1, 21, 11, 55, 0, TimeScalesFactory.getUTC()),
602                                 file.getHeader().getCreationDate());
603         Assertions.assertEquals("GSFC", file.getHeader().getOriginator());
604         Assertions.assertEquals("A000001", file.getHeader().getMessageId());
605 
606         Segment<AdmMetadata, ApmData> segment = file.getSegments().get(0);
607 
608         // Check Metadata Block
609         Assertions.assertEquals("SPINNING",   segment.getMetadata().getComments().get(0));
610         Assertions.assertEquals("MMS1",       segment.getMetadata().getObjectName());
611         Assertions.assertEquals("2015-011A",  segment.getMetadata().getObjectID());
612         Assertions.assertEquals(2015,         segment.getMetadata().getLaunchYear());
613         Assertions.assertEquals(11,           segment.getMetadata().getLaunchNumber());
614         Assertions.assertEquals("A",          segment.getMetadata().getLaunchPiece());
615         Assertions.assertEquals("EARTH",      segment.getMetadata().getCenter().getName());
616         Assertions.assertTrue(segment.getMetadata().getHasCreatableBody());
617         Assertions.assertEquals(CelestialBodyFactory.getEarth(), segment.getMetadata().getCenter().getBody());
618         Assertions.assertEquals("TAI",        segment.getMetadata().getTimeSystem().name());
619 
620         // Check data block
621         Assertions.assertEquals(new AbsoluteDate(2023, 1, 1, 0, 0, 0.0,
622                                                  TimeScalesFactory.getTAI()),
623                                 segment.getData().getEpoch());
624         Assertions.assertFalse(segment.getData().hasManeuvers());
625         Assertions.assertEquals(CelestialBodyFrame.J2000, segment.getData().getSpinStabilizedBlock().getEndpoints().getFrameA().asCelestialBodyFrame());
626         Assertions.assertEquals(SpacecraftBodyFrame.BaseEquipment.SC_BODY,
627                                 segment.getData().getSpinStabilizedBlock().getEndpoints().getFrameB().asSpacecraftBodyFrame().getBaseEquipment());
628         Assertions.assertEquals("1", segment.getData().getSpinStabilizedBlock().getEndpoints().getFrameB().asSpacecraftBodyFrame().getLabel());
629         Assertions.assertEquals(10.0, FastMath.toDegrees(segment.getData().getSpinStabilizedBlock().getSpinAlpha()),     ANGLE_PRECISION);
630         Assertions.assertEquals(30.0, FastMath.toDegrees(segment.getData().getSpinStabilizedBlock().getSpinDelta()),     ANGLE_PRECISION);
631         Assertions.assertEquals( 0.0, FastMath.toDegrees(segment.getData().getSpinStabilizedBlock().getSpinAngle()),     ANGLE_PRECISION);
632         Assertions.assertEquals(80.0, FastMath.toDegrees(segment.getData().getSpinStabilizedBlock().getMomentumAlpha()), ANGLE_PRECISION);
633         Assertions.assertEquals(10.0, FastMath.toDegrees(segment.getData().getSpinStabilizedBlock().getMomentumDelta()), ANGLE_PRECISION);
634         Assertions.assertEquals( 0.5, FastMath.toDegrees(segment.getData().getSpinStabilizedBlock().getNutationVel()),   ANGLE_PRECISION);
635 
636         Attitude attitude = file.getAttitude(null, null);
637         Assertions.assertEquals(segment.getData().getEpoch(), attitude.getDate());
638         Assertions.assertEquals(2.22728e-2, attitude.getSpin().getNorm(), 1.0e-7);
639 
640     }
641 
642     @Test
643     public void testParseAPM8() {
644 
645         // File
646         final String ex = "/ccsds/adm/apm/APMExample08.txt";
647 
648         // Initialize the parser
649         final ApmParser parser = new ParserBuilder().buildApmParser();
650 
651         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
652 
653         // Generated APM file
654         final Apm file = parser.parseMessage(source);
655 
656         // Verify general data
657         Assertions.assertEquals(IERSConventions.IERS_2010, file.getConventions());
658         Assertions.assertEquals(DataContext.getDefault(),  file.getDataContext());
659 
660         // Check Header Block
661         Assertions.assertEquals(2.0, file.getHeader().getFormatVersion(), 1.0e-10);
662         Assertions.assertEquals(new AbsoluteDate(2023, 2, 3, 12, 0, 0, TimeScalesFactory.getUTC()),
663                             file.getHeader().getCreationDate());
664         Assertions.assertEquals("GSFC",    file.getHeader().getOriginator());
665         Assertions.assertEquals("A000002", file.getHeader().getMessageId());
666 
667         Segment<AdmMetadata, ApmData> segment = file.getSegments().get(0);
668 
669         // Check Metadata Block
670         Assertions.assertEquals("Rotation From Nadir", segment.getMetadata().getComments().get(0));
671         Assertions.assertEquals("LRO",        segment.getMetadata().getObjectName());
672         Assertions.assertEquals("2009-031A",  segment.getMetadata().getObjectID());
673         Assertions.assertEquals(2009,         segment.getMetadata().getLaunchYear());
674         Assertions.assertEquals(31,           segment.getMetadata().getLaunchNumber());
675         Assertions.assertEquals("A",          segment.getMetadata().getLaunchPiece());
676         Assertions.assertEquals("MOON",       segment.getMetadata().getCenter().getName());
677         Assertions.assertTrue(segment.getMetadata().getHasCreatableBody());
678         Assertions.assertEquals(CelestialBodyFactory.getMoon(), segment.getMetadata().getCenter().getBody());
679         Assertions.assertEquals("TAI",        segment.getMetadata().getTimeSystem().name());
680 
681         // Check data block
682         Assertions.assertEquals(new AbsoluteDate(2023, 1, 1, 0, 0, 0.0, TimeScalesFactory.getTAI()),
683                                 segment.getData().getEpoch());
684         Assertions.assertFalse(segment.getData().hasManeuvers());
685         Assertions.assertEquals(CelestialBodyFrame.EME2000, segment.getData().getQuaternionBlock().getEndpoints().getFrameA().asCelestialBodyFrame());
686         Assertions.assertEquals(SpacecraftBodyFrame.BaseEquipment.SC_BODY,
687                                 segment.getData().getQuaternionBlock().getEndpoints().getFrameB().asSpacecraftBodyFrame().getBaseEquipment());
688         Assertions.assertEquals("", segment.getData().getQuaternionBlock().getEndpoints().getFrameB().asSpacecraftBodyFrame().getLabel());
689         Assertions.assertEquals( 0.925417, segment.getData().getQuaternionBlock().getQuaternion().getQ0(), QUATERNION_PRECISION);
690         Assertions.assertEquals( 0.171010, segment.getData().getQuaternionBlock().getQuaternion().getQ1(), QUATERNION_PRECISION);
691         Assertions.assertEquals(-0.030154, segment.getData().getQuaternionBlock().getQuaternion().getQ2(), QUATERNION_PRECISION);
692         Assertions.assertEquals( 0.336824, segment.getData().getQuaternionBlock().getQuaternion().getQ3(), QUATERNION_PRECISION);
693         Assertions.assertTrue(Double.isNaN(segment.getData().getQuaternionBlock().getQuaternionDot().getQ0()));
694         Assertions.assertTrue(Double.isNaN(segment.getData().getQuaternionBlock().getQuaternionDot().getQ1()));
695         Assertions.assertTrue(Double.isNaN(segment.getData().getQuaternionBlock().getQuaternionDot().getQ2()));
696         Assertions.assertTrue(Double.isNaN(segment.getData().getQuaternionBlock().getQuaternionDot().getQ3()));
697 
698         Assertions.assertEquals(CelestialBodyFrame.EME2000, segment.getData().getAngularVelocityBlock().getEndpoints().getFrameA().asCelestialBodyFrame());
699         Assertions.assertEquals(SpacecraftBodyFrame.BaseEquipment.SC_BODY,
700                                 segment.getData().getAngularVelocityBlock().getEndpoints().getFrameB().asSpacecraftBodyFrame().getBaseEquipment());
701         Assertions.assertEquals("", segment.getData().getAngularVelocityBlock().getEndpoints().getFrameB().asSpacecraftBodyFrame().getLabel());
702         Assertions.assertEquals( 0.0001,  FastMath.toDegrees(segment.getData().getAngularVelocityBlock().getAngVelX()), ANGLE_PRECISION);
703         Assertions.assertEquals( 0.05,    FastMath.toDegrees(segment.getData().getAngularVelocityBlock().getAngVelY()), ANGLE_PRECISION);
704         Assertions.assertEquals( 0.00003, FastMath.toDegrees(segment.getData().getAngularVelocityBlock().getAngVelZ()), ANGLE_PRECISION);
705 
706     }
707 
708     @Test
709     public void testParseAPM9() {
710 
711         // File
712         final String ex = "/ccsds/adm/apm/APMExample09.txt";
713 
714         // Initialize the parser
715         final ApmParser parser = new ParserBuilder().buildApmParser();
716 
717         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
718 
719         // Generated APM file
720         final Apm file = parser.parseMessage(source);
721 
722         // Verify general data
723         Assertions.assertEquals(IERSConventions.IERS_2010, file.getConventions());
724         Assertions.assertEquals(DataContext.getDefault(),  file.getDataContext());
725 
726         // Check Header Block
727         Assertions.assertEquals(2.0, file.getHeader().getFormatVersion(), 1.0e-10);
728         Assertions.assertEquals(new AbsoluteDate(2023, 2, 5, 12, 0, 0, TimeScalesFactory.getUTC()),
729                                 file.getHeader().getCreationDate());
730         Assertions.assertEquals("GSFC", file.getHeader().getOriginator());
731         Assertions.assertEquals("A000003", file.getHeader().getMessageId());
732 
733         Segment<AdmMetadata, ApmData> segment = file.getSegments().get(0);
734 
735         Assertions.assertEquals("SPINNING",   segment.getMetadata().getComments().get(0));
736         Assertions.assertEquals("MMS1",       segment.getMetadata().getObjectName());
737         Assertions.assertEquals("2015-011A",  segment.getMetadata().getObjectID());
738         Assertions.assertEquals(2015,         segment.getMetadata().getLaunchYear());
739         Assertions.assertEquals(11,           segment.getMetadata().getLaunchNumber());
740         Assertions.assertEquals("A",          segment.getMetadata().getLaunchPiece());
741         Assertions.assertEquals("EARTH",      segment.getMetadata().getCenter().getName());
742         Assertions.assertTrue(segment.getMetadata().getHasCreatableBody());
743         Assertions.assertEquals(CelestialBodyFactory.getEarth(), segment.getMetadata().getCenter().getBody());
744         Assertions.assertEquals("TAI",        segment.getMetadata().getTimeSystem().name());
745 
746         // Check data block
747         Assertions.assertEquals(new AbsoluteDate(2023, 1, 1, 0, 0, 0.0,
748                                                  TimeScalesFactory.getTAI()),
749                                 segment.getData().getEpoch());
750         Assertions.assertNull(segment.getData().getQuaternionBlock());
751 
752         Assertions.assertEquals(CelestialBodyFrame.EME2000, segment.getData().getEulerBlock().getEndpoints().getFrameA().asCelestialBodyFrame());
753         Assertions.assertEquals(SpacecraftBodyFrame.BaseEquipment.SC_BODY,
754                                 segment.getData().getEulerBlock().getEndpoints().getFrameB().asSpacecraftBodyFrame().getBaseEquipment());
755         Assertions.assertEquals("", segment.getData().getEulerBlock().getEndpoints().getFrameB().asSpacecraftBodyFrame().getLabel());
756         Assertions.assertEquals(RotationOrder.ZXZ, segment.getData().getEulerBlock().getEulerRotSeq());
757         Assertions.assertEquals(10.0, FastMath.toDegrees(segment.getData().getEulerBlock().getRotationAngles()[0]), ANGLE_PRECISION);
758         Assertions.assertEquals(20.0, FastMath.toDegrees(segment.getData().getEulerBlock().getRotationAngles()[1]), ANGLE_PRECISION);
759         Assertions.assertEquals( 0.0, FastMath.toDegrees(segment.getData().getEulerBlock().getRotationAngles()[2]), ANGLE_PRECISION);
760 
761         Assertions.assertEquals(CelestialBodyFrame.EME2000, segment.getData().getSpinStabilizedBlock().getEndpoints().getFrameA().asCelestialBodyFrame());
762         Assertions.assertEquals(SpacecraftBodyFrame.BaseEquipment.SC_BODY,
763                                 segment.getData().getSpinStabilizedBlock().getEndpoints().getFrameB().asSpacecraftBodyFrame().getBaseEquipment());
764         Assertions.assertEquals("", segment.getData().getSpinStabilizedBlock().getEndpoints().getFrameB().asSpacecraftBodyFrame().getLabel());
765         Assertions.assertEquals(-80.0, FastMath.toDegrees(segment.getData().getSpinStabilizedBlock().getSpinAlpha()),      ANGLE_PRECISION);
766         Assertions.assertEquals( 70.0, FastMath.toDegrees(segment.getData().getSpinStabilizedBlock().getSpinDelta()),      ANGLE_PRECISION);
767         Assertions.assertEquals(  0.0, FastMath.toDegrees(segment.getData().getSpinStabilizedBlock().getSpinAngle()),      ANGLE_PRECISION);
768         Assertions.assertEquals(  1.0, FastMath.toDegrees(segment.getData().getSpinStabilizedBlock().getSpinAngleVel()),   ANGLE_PRECISION);
769         Assertions.assertEquals(  0.1, FastMath.toDegrees(segment.getData().getSpinStabilizedBlock().getNutation()),       ANGLE_PRECISION);
770         Assertions.assertEquals(720.0, segment.getData().getSpinStabilizedBlock().getNutationPeriod(), 1.0e-10);
771         Assertions.assertEquals(-85.0, FastMath.toDegrees(segment.getData().getSpinStabilizedBlock().getNutationPhase()),  ANGLE_PRECISION);
772 
773         Assertions.assertEquals(SpacecraftBodyFrame.BaseEquipment.SC_BODY,
774                                 segment.getData().getInertiaBlock().getFrame().asSpacecraftBodyFrame().getBaseEquipment());
775         Assertions.assertEquals("", segment.getData().getInertiaBlock().getFrame().asSpacecraftBodyFrame().getLabel());
776         Assertions.assertEquals(1443.10, segment.getData().getInertiaBlock().getInertiaMatrix().getEntry(0, 0), 1.0e-10);
777         Assertions.assertEquals(1445.20, segment.getData().getInertiaBlock().getInertiaMatrix().getEntry(1, 1), 1.0e-10);
778         Assertions.assertEquals(1760.20, segment.getData().getInertiaBlock().getInertiaMatrix().getEntry(2, 2), 1.0e-10);
779         Assertions.assertEquals( -86.40, segment.getData().getInertiaBlock().getInertiaMatrix().getEntry(0, 1), 1.0e-10);
780         Assertions.assertEquals(   0.00, segment.getData().getInertiaBlock().getInertiaMatrix().getEntry(0, 2), 1.0e-10);
781         Assertions.assertEquals(  -0.09, segment.getData().getInertiaBlock().getInertiaMatrix().getEntry(1, 2), 1.0e-10);
782 
783         Assertions.assertEquals(SpacecraftBodyFrame.BaseEquipment.SC_BODY,
784                                 segment.getData().getManeuver(0).getFrame().asSpacecraftBodyFrame().getBaseEquipment());
785         Assertions.assertEquals("",      segment.getData().getManeuver(0).getFrame().asSpacecraftBodyFrame().getLabel());
786         Assertions.assertEquals(1.0,     segment.getData().getManeuver(0).getDuration(),      1.0e-10);
787         Assertions.assertEquals(1.0,     segment.getData().getManeuver(0).getTorque().getX(), 1.0e-10);
788         Assertions.assertEquals(0.0,     segment.getData().getManeuver(0).getTorque().getY(), 1.0e-10);
789         Assertions.assertEquals(0.0,     segment.getData().getManeuver(0).getTorque().getZ(), 1.0e-10);
790         Assertions.assertEquals(-0.001,  segment.getData().getManeuver(0).getDeltaMass(),     1.0e-10);
791 
792     }
793 
794     @Test
795     public void testOrbitRelativeFrameInertial() {
796 
797         // File
798         final String ex = "/ccsds/adm/apm/APM-orbit-relative-frame-inertial.txt";
799 
800         // Initialize the parser
801         final ApmParser parser = new ParserBuilder().
802                                  withMissionReferenceDate(new AbsoluteDate("2002-09-30T14:28:15.117",
803                                                                            TimeScalesFactory.getUTC())).
804                                  buildApmParser();
805 
806         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
807 
808         // Generated APM file
809         final Apm file = parser.parseMessage(source);
810 
811         // Verify general data
812         Assertions.assertEquals(IERSConventions.IERS_2010, file.getConventions());
813         Assertions.assertEquals(DataContext.getDefault(),  file.getDataContext());
814 
815         // Check Header Block
816         Assertions.assertEquals(1.0, file.getHeader().getFormatVersion(), 1.0e-10);
817         Assertions.assertEquals(new AbsoluteDate(2021, 2, 24, 18, 59, 43,
818                                              TimeScalesFactory.getUTC()),
819                             file.getHeader().getCreationDate());
820         Assertions.assertEquals("CS GROUP", file.getHeader().getOriginator());
821 
822         Segment<AdmMetadata, ApmData> segment = file.getSegments().get(0);
823 
824         // Check Metadata Block
825         Assertions.assertEquals("DUMMY",      segment.getMetadata().getObjectName());
826         Assertions.assertEquals("9999-111Z",  segment.getMetadata().getObjectID());
827         Assertions.assertEquals(9999,         segment.getMetadata().getLaunchYear());
828         Assertions.assertEquals(111,          segment.getMetadata().getLaunchNumber());
829         Assertions.assertEquals("Z",          segment.getMetadata().getLaunchPiece());
830         Assertions.assertEquals("JUPITER",    segment.getMetadata().getCenter().getName());
831         Assertions.assertTrue(segment.getMetadata().getHasCreatableBody());
832         Assertions.assertEquals(CelestialBodyFactory.getJupiter(), segment.getMetadata().getCenter().getBody());
833         Assertions.assertEquals("TDB",        segment.getMetadata().getTimeSystem().name());
834 
835         // Check data block
836         Assertions.assertFalse(segment.getData().hasManeuvers());
837         Assertions.assertEquals(SpacecraftBodyFrame.BaseEquipment.SC_BODY,
838                             segment.getData().getQuaternionBlock().getEndpoints().getFrameA().asSpacecraftBodyFrame().getBaseEquipment());
839         Assertions.assertEquals("1", segment.getData().getQuaternionBlock().getEndpoints().getFrameA().asSpacecraftBodyFrame().getLabel());
840         Assertions.assertEquals(OrbitRelativeFrame.VNC_INERTIAL, segment.getData().getQuaternionBlock().getEndpoints().getFrameB().asOrbitRelativeFrame());
841         Assertions.assertFalse(segment.getData().getQuaternionBlock().getEndpoints().isA2b());
842         Assertions.assertEquals(new AbsoluteDate(2021, 1, 1, 0, 0, 0.0, TimeScalesFactory.getTDB()),
843                             segment.getData().getEpoch());
844 
845         final PVCoordinates pv = new PVCoordinates(new Vector3D( 1.234e7, -0.567e7, 9.876e6),
846                                                    new Vector3D(-0.772e4,  5.002e4, 4.892e2));
847         Attitude attitude = file.getAttitude(FramesFactory.getEME2000(),
848                                              (date, frame) -> new TimeStampedPVCoordinates(date, pv));
849         Vector3D xSat = attitude.getRotation().applyInverseTo(Vector3D.PLUS_I);
850         Vector3D ySat = attitude.getRotation().applyInverseTo(Vector3D.PLUS_J);
851         Assertions.assertEquals(FastMath.PI, Vector3D.angle(xSat, pv.getVelocity()), 1.0e-10);
852         Assertions.assertEquals(0.0,         Vector3D.angle(ySat, pv.getMomentum()), 1.0e-10);
853         Assertions.assertEquals(0.0,         attitude.getSpin().getX(), 1.0e-10);
854         Assertions.assertEquals(0.0,         attitude.getSpin().getY(), 1.0e-10);
855         Assertions.assertEquals(0.0,         attitude.getSpin().getZ(), 1.0e-10);
856 
857     }
858 
859     @Test
860     public void testOrbitRelativeFrameRotating() {
861 
862         // File
863         final String ex = "/ccsds/adm/apm/APM-orbit-relative-frame-rotating.txt";
864 
865         // Initialize the parser
866         final ApmParser parser = new ParserBuilder().
867                                  withMissionReferenceDate(new AbsoluteDate("2002-09-30T14:28:15.117",
868                                                                            TimeScalesFactory.getUTC())).
869                                  buildApmParser();
870 
871         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
872 
873         // Generated APM file
874         final Apm file = parser.parseMessage(source);
875 
876         // Verify general data
877         Assertions.assertEquals(IERSConventions.IERS_2010, file.getConventions());
878         Assertions.assertEquals(DataContext.getDefault(),  file.getDataContext());
879 
880         // Check Header Block
881         Assertions.assertEquals(1.0, file.getHeader().getFormatVersion(), 1.0e-10);
882         Assertions.assertEquals(new AbsoluteDate(2021, 2, 24, 18, 59, 43,
883                                              TimeScalesFactory.getUTC()),
884                             file.getHeader().getCreationDate());
885         Assertions.assertEquals("CS GROUP", file.getHeader().getOriginator());
886 
887         Segment<AdmMetadata, ApmData> segment = file.getSegments().get(0);
888 
889         // Check Metadata Block
890         Assertions.assertEquals("DUMMY",      segment.getMetadata().getObjectName());
891         Assertions.assertEquals("9999-111Z",  segment.getMetadata().getObjectID());
892         Assertions.assertEquals(9999,         segment.getMetadata().getLaunchYear());
893         Assertions.assertEquals(111,          segment.getMetadata().getLaunchNumber());
894         Assertions.assertEquals("Z",          segment.getMetadata().getLaunchPiece());
895         Assertions.assertEquals("JUPITER",    segment.getMetadata().getCenter().getName());
896         Assertions.assertTrue(segment.getMetadata().getHasCreatableBody());
897         Assertions.assertEquals(CelestialBodyFactory.getJupiter(), segment.getMetadata().getCenter().getBody());
898         Assertions.assertEquals("TDB",        segment.getMetadata().getTimeSystem().name());
899 
900         // Check data block
901         Assertions.assertFalse(segment.getData().hasManeuvers());
902         Assertions.assertEquals(SpacecraftBodyFrame.BaseEquipment.SC_BODY,
903                             segment.getData().getQuaternionBlock().getEndpoints().getFrameA().asSpacecraftBodyFrame().getBaseEquipment());
904         Assertions.assertEquals("1", segment.getData().getQuaternionBlock().getEndpoints().getFrameA().asSpacecraftBodyFrame().getLabel());
905         Assertions.assertEquals(OrbitRelativeFrame.VNC_ROTATING, segment.getData().getQuaternionBlock().getEndpoints().getFrameB().asOrbitRelativeFrame());
906         Assertions.assertFalse(segment.getData().getQuaternionBlock().getEndpoints().isA2b());
907         Assertions.assertEquals(new AbsoluteDate(2021, 1, 1, 0, 0, 0.0, TimeScalesFactory.getTDB()),
908                             segment.getData().getEpoch());
909 
910         PVCoordinates pv = new PVCoordinates(new Vector3D( 1.234e7, -0.567e7, 9.876e6),
911                                              new Vector3D(-0.772e4,  5.002e4, 4.892e2));
912         Attitude attitude = file.getAttitude(FramesFactory.getEME2000(),
913                                              (date, frame) -> new TimeStampedPVCoordinates(date, pv));
914         Vector3D xSat = attitude.getRotation().applyInverseTo(Vector3D.PLUS_I);
915         Vector3D ySat = attitude.getRotation().applyInverseTo(Vector3D.PLUS_J);
916         Assertions.assertEquals(FastMath.PI, Vector3D.angle(xSat, pv.getVelocity()), 1.0e-10);
917         Assertions.assertEquals(0.0,         Vector3D.angle(ySat, pv.getMomentum()), 1.0e-10);
918         Assertions.assertEquals(0.0,                               attitude.getSpin().getX(), 1.0e-10);
919         Assertions.assertEquals(pv.getAngularVelocity().getNorm(), attitude.getSpin().getY(), 1.0e-10);
920         Assertions.assertEquals(0.0,                               attitude.getSpin().getZ(), 1.0e-10);
921 
922     }
923 
924     @Test
925     public void testUnsupportedOrbitRelativeFrame() {
926 
927         // File
928         final String ex = "/ccsds/adm/apm/APM-unsupported-orbit-relative-frame.txt";
929 
930         // Initialize the parser
931         final ApmParser parser = new ParserBuilder().
932                                  withMissionReferenceDate(new AbsoluteDate("2002-09-30T14:28:15.117",
933                                                                            TimeScalesFactory.getUTC())).
934                                  buildApmParser();
935 
936         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
937 
938         // Generated APM file
939         final Apm file = parser.parseMessage(source);
940 
941         // Verify general data
942         Assertions.assertEquals(IERSConventions.IERS_2010, file.getConventions());
943         Assertions.assertEquals(DataContext.getDefault(),  file.getDataContext());
944 
945         // Check Header Block
946         Assertions.assertEquals(1.0, file.getHeader().getFormatVersion(), 1.0e-10);
947         Assertions.assertEquals(new AbsoluteDate(2021, 2, 24, 18, 59, 43,
948                                              TimeScalesFactory.getUTC()),
949                             file.getHeader().getCreationDate());
950         Assertions.assertEquals("CS GROUP", file.getHeader().getOriginator());
951 
952         Segment<AdmMetadata, ApmData> segment = file.getSegments().get(0);
953 
954         // Check Metadata Block
955         Assertions.assertEquals("DUMMY",      segment.getMetadata().getObjectName());
956         Assertions.assertEquals("9999-111Z",  segment.getMetadata().getObjectID());
957         Assertions.assertEquals(9999,         segment.getMetadata().getLaunchYear());
958         Assertions.assertEquals(111,          segment.getMetadata().getLaunchNumber());
959         Assertions.assertEquals("Z",          segment.getMetadata().getLaunchPiece());
960         Assertions.assertEquals("JUPITER",    segment.getMetadata().getCenter().getName());
961         Assertions.assertTrue(segment.getMetadata().getHasCreatableBody());
962         Assertions.assertEquals(CelestialBodyFactory.getJupiter(), segment.getMetadata().getCenter().getBody());
963         Assertions.assertEquals("TDB",        segment.getMetadata().getTimeSystem().name());
964 
965         // Check data block
966         Assertions.assertFalse(segment.getData().hasManeuvers());
967         Assertions.assertEquals(SpacecraftBodyFrame.BaseEquipment.SC_BODY,
968                             segment.getData().getQuaternionBlock().getEndpoints().getFrameA().asSpacecraftBodyFrame().getBaseEquipment());
969         Assertions.assertEquals("1", segment.getData().getQuaternionBlock().getEndpoints().getFrameA().asSpacecraftBodyFrame().getLabel());
970         Assertions.assertEquals(OrbitRelativeFrame.SEZ_INERTIAL, segment.getData().getQuaternionBlock().getEndpoints().getFrameB().asOrbitRelativeFrame());
971         Assertions.assertTrue(segment.getData().getQuaternionBlock().getEndpoints().isA2b());
972         Assertions.assertEquals(new AbsoluteDate(2021, 1, 1, 0, 0, 0.0, TimeScalesFactory.getTDB()),
973                             segment.getData().getEpoch());
974 
975         final PVCoordinatesProvider prov = (date, frame) -> new TimeStampedPVCoordinates(date,
976                                                                                          new PVCoordinates(new Vector3D( 1.234e7, -0.567e7, 9.876e6),
977                                                                                                            new Vector3D(-0.772e4,  5.002e4, 4.892e2)));
978         try {
979             file.getAttitude(FramesFactory.getEME2000(), prov);
980             Assertions.fail("an exception should have been thrown");
981         } catch (OrekitException oe) {
982             Assertions.assertEquals(OrekitMessages.UNSUPPORTED_LOCAL_ORBITAL_FRAME, oe.getSpecifier());
983             Assertions.assertEquals(OrbitRelativeFrame.SEZ_INERTIAL.name(), oe.getParts()[0]);
984         }
985 
986     }
987 
988     @Test
989     public void testUnknownFrame() {
990 
991         // File
992         final String ex = "/ccsds/adm/apm/APM-unknown-frame.txt";
993 
994         // Initialize the parser
995         final ApmParser parser = new ParserBuilder().
996                                  withMissionReferenceDate(new AbsoluteDate("2002-09-30T14:28:15.117",
997                                                                            TimeScalesFactory.getUTC())).
998                                  buildApmParser();
999 
1000         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
1001 
1002         // Generated APM file
1003         final Apm file = parser.parseMessage(source);
1004 
1005         // Verify general data
1006         Assertions.assertEquals(IERSConventions.IERS_2010, file.getConventions());
1007         Assertions.assertEquals(DataContext.getDefault(),  file.getDataContext());
1008 
1009         // Check Header Block
1010         Assertions.assertEquals(1.0, file.getHeader().getFormatVersion(), 1.0e-10);
1011         Assertions.assertEquals(new AbsoluteDate(2021, 2, 24, 18, 59, 43,
1012                                              TimeScalesFactory.getUTC()),
1013                             file.getHeader().getCreationDate());
1014         Assertions.assertEquals("CS GROUP", file.getHeader().getOriginator());
1015 
1016         Segment<AdmMetadata, ApmData> segment = file.getSegments().get(0);
1017 
1018         // Check Metadata Block
1019         Assertions.assertEquals("DUMMY",      segment.getMetadata().getObjectName());
1020         Assertions.assertEquals("9999-111Z",  segment.getMetadata().getObjectID());
1021         Assertions.assertEquals(9999,         segment.getMetadata().getLaunchYear());
1022         Assertions.assertEquals(111,          segment.getMetadata().getLaunchNumber());
1023         Assertions.assertEquals("Z",          segment.getMetadata().getLaunchPiece());
1024         Assertions.assertEquals("JUPITER",    segment.getMetadata().getCenter().getName());
1025         Assertions.assertTrue(segment.getMetadata().getHasCreatableBody());
1026         Assertions.assertEquals(CelestialBodyFactory.getJupiter(), segment.getMetadata().getCenter().getBody());
1027         Assertions.assertEquals("TDB",        segment.getMetadata().getTimeSystem().name());
1028 
1029         // Check data block
1030         Assertions.assertFalse(segment.getData().hasManeuvers());
1031         FrameFacade sb = segment.getData().getQuaternionBlock().getEndpoints().getSpacecraftBodyFrame();
1032         Assertions.assertEquals(SpacecraftBodyFrame.BaseEquipment.SC_BODY, sb.asSpacecraftBodyFrame().getBaseEquipment());
1033         Assertions.assertEquals("1", sb.asSpacecraftBodyFrame().getLabel());
1034         FrameFacade ext = segment.getData().getQuaternionBlock().getEndpoints().getExternalFrame();
1035         Assertions.assertNull(ext.asFrame());
1036         Assertions.assertNull(ext.asCelestialBodyFrame());
1037         Assertions.assertNull(ext.asOrbitRelativeFrame());
1038         Assertions.assertNull(ext.asSpacecraftBodyFrame());
1039         Assertions.assertEquals("UNKNOWN", ext.getName());
1040         Assertions.assertFalse(segment.getData().getQuaternionBlock().getEndpoints().isA2b());
1041         Assertions.assertEquals(new AbsoluteDate(2021, 1, 1, 0, 0, 0.0, TimeScalesFactory.getTDB()),
1042                             segment.getData().getEpoch());
1043 
1044         try {
1045             file.getAttitude(FramesFactory.getEME2000(),
1046                              (date, frame) -> new TimeStampedPVCoordinates(date,
1047                                                                            new PVCoordinates(new Vector3D( 1.234e7, -0.567e7, 9.876e6),
1048                                                                                              new Vector3D(-0.772e4,  5.002e4, 4.892e2))));
1049             Assertions.fail("an exception should have been thrown");
1050         } catch (OrekitException oe) {
1051             Assertions.assertEquals(OrekitMessages.CCSDS_INVALID_FRAME, oe.getSpecifier());
1052             Assertions.assertEquals(ext.getName(), oe.getParts()[0]);
1053         }
1054 
1055     }
1056 
1057     @Test
1058     public void testAttitudeQuaternion() {
1059         doTestAttitude("/ccsds/adm/apm/APM-quaternion.xml");
1060     }
1061 
1062     @Test
1063     public void testAtitudeQuaternionRates() {
1064         doTestAttitude("/ccsds/adm/apm/APM-quaternion-rates.xml");
1065     }
1066 
1067     @Test
1068     public void testAtitudeQuaternionAngvel() {
1069         doTestAttitude("/ccsds/adm/apm/APM-quaternion-angvel.xml");
1070     }
1071 
1072     @Test
1073     public void testAtitudeQuaternionEulerAnglesRates() {
1074         doTestAttitude("/ccsds/adm/apm/APM-quaternion-euler-angles-rates.xml");
1075     }
1076 
1077     @Test
1078     public void testAtitudeQuaternionEulerRates() {
1079         doTestAttitude("/ccsds/adm/apm/APM-quaternion-euler-rates.xml");
1080     }
1081 
1082     @Test
1083     public void testAtitudeEulerAnglesRates() {
1084         doTestAttitude("/ccsds/adm/apm/APM-euler-angles-rates.xml");
1085     }
1086 
1087     @Test
1088     public void testAtitudeEulerAnglesAngvel() {
1089         doTestAttitude("/ccsds/adm/apm/APM-euler-angles-angvel.xml");
1090     }
1091 
1092     @Test
1093     public void testAtitudeEulerAngles() {
1094         doTestAttitude("/ccsds/adm/apm/APM-euler-angles.xml");
1095     }
1096 
1097     @Test
1098     public void testAtitudeSpin() {
1099         doTestAttitude("/ccsds/adm/apm/APM-spin.xml");
1100     }
1101 
1102     @Test
1103     public void testAtitudeSpinNutation() {
1104         doTestAttitude("/ccsds/adm/apm/APM-spin-nutation.xml");
1105     }
1106 
1107     @Test
1108     public void testAtitudeSpinNutationMomentum() {
1109         doTestAttitude("/ccsds/adm/apm/APM-spin-nutation-momentum.xml");
1110     }
1111 
1112     private void doTestAttitude(final String name) {
1113         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
1114         final Apm apm = new ParserBuilder().
1115                         withMissionReferenceDate(AbsoluteDate.J2000_EPOCH).
1116                         buildApmParser().
1117                         parseMessage(source);
1118         final Attitude attitude = apm.getAttitude(FramesFactory.getEME2000(), null);
1119         Assertions.assertEquals(0.0,
1120                                 attitude.getDate().durationFrom(new AbsoluteDate(2008, 4, 7, TimeScalesFactory.getUTC())),
1121                                 1.0e-15);
1122         final double sign = FastMath.copySign(1.0, attitude.getRotation().getQ0());
1123         Assertions.assertEquals( 0.6184633325084984,  sign * attitude.getRotation().getQ0(), 1.0e-15);
1124         Assertions.assertEquals(-0.6447327809733585,  sign * attitude.getRotation().getQ1(), 1.0e-15);
1125         Assertions.assertEquals(-0.3463409538535587,  sign * attitude.getRotation().getQ2(), 1.0e-15);
1126         Assertions.assertEquals(-0.28613054916357517, sign * attitude.getRotation().getQ3(), 1.0e-15);
1127 
1128     }
1129 
1130     @Test
1131     public void testNoLogicalBlocks() {
1132         try {
1133             final String name = "/ccsds/adm/apm/APM-no-logical-blocks.txt";
1134             final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
1135             new ParserBuilder().
1136             withMissionReferenceDate(AbsoluteDate.J2000_EPOCH).
1137             buildApmParser().
1138             parseMessage(source);
1139             Assertions.fail("an exception should have been thrown");
1140         } catch (OrekitException oe) {
1141             Assertions.assertEquals(OrekitMessages.CCSDS_INCOMPLETE_DATA, oe.getSpecifier());
1142         }
1143     }
1144 
1145     @Test
1146     public void testNoAttitude() {
1147         final String name = "/ccsds/adm/apm/APM-no-attitude.txt";
1148         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
1149         final Apm apm = new ParserBuilder().
1150                         withMissionReferenceDate(AbsoluteDate.J2000_EPOCH).
1151                         buildApmParser().
1152                         parseMessage(source);
1153         try {
1154             apm.getAttitude(FramesFactory.getGCRF(), null);
1155             Assertions.fail("an exception should have been thrown");
1156         } catch (OrekitException oe) {
1157             Assertions.assertEquals(OrekitMessages.CCSDS_INCOMPLETE_DATA, oe.getSpecifier());
1158         }
1159     }
1160 
1161     @Test
1162     public void testNotImplementedTimeSystems() {
1163         try {
1164             final String name = "/ccsds/adm/apm/APM-inconsistent-time-systems.txt";
1165             final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
1166             new ParserBuilder().
1167             withMissionReferenceDate(AbsoluteDate.J2000_EPOCH).
1168             buildApmParser().
1169             parseMessage(source);
1170             Assertions.fail("an exception should have been thrown");
1171         } catch (OrekitException oe) {
1172             Assertions.assertEquals(OrekitMessages.CCSDS_TIME_SYSTEM_NOT_IMPLEMENTED, oe.getSpecifier());
1173             Assertions.assertEquals("BCE", oe.getParts()[0]);
1174         }
1175     }
1176 
1177     @Test
1178     public void testWrongADMType() {
1179         final String name = "/ccsds/adm/aem/AEMExample01.txt";
1180         try {
1181             final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
1182             new ParserBuilder().
1183             withMissionReferenceDate(AbsoluteDate.J2000_EPOCH).
1184             buildApmParser().
1185             parseMessage(source);
1186             Assertions.fail("an exception should have been thrown");
1187         } catch (OrekitException oe) {
1188             Assertions.assertEquals(OrekitMessages.UNSUPPORTED_FILE_FORMAT, oe.getSpecifier());
1189             Assertions.assertEquals(name, oe.getParts()[0]);
1190         }
1191     }
1192 
1193     @Test
1194     public void testNumberFormatErrorType() {
1195         final String name = "/ccsds/adm/apm/APM-number-format-error.txt";
1196         try {
1197             final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
1198             new ParserBuilder().
1199             withMissionReferenceDate(AbsoluteDate.J2000_EPOCH).
1200             buildApmParser().
1201             parseMessage(source);
1202             Assertions.fail("an exception should have been thrown");
1203         } catch (OrekitException oe) {
1204             Assertions.assertEquals(OrekitMessages.UNABLE_TO_PARSE_ELEMENT_IN_FILE, oe.getSpecifier());
1205             Assertions.assertEquals("Q1", oe.getParts()[0]);
1206             Assertions.assertEquals(22, oe.getParts()[1]);
1207             Assertions.assertEquals(name, oe.getParts()[2]);
1208         }
1209     }
1210 
1211 
1212     @Test
1213     public void testNonExistentFile() throws URISyntaxException {
1214         final String realName = getClass().getResource("/ccsds/adm/apm/APMExample01.txt").toURI().getPath();
1215         final String wrongName = realName + "xxxxx";
1216         try {
1217             final DataSource source = new DataSource(wrongName, () -> getClass().getResourceAsStream(wrongName));
1218             new ParserBuilder().
1219             withMissionReferenceDate(AbsoluteDate.J2000_EPOCH).
1220             buildApmParser().
1221             parseMessage(source);
1222             Assertions.fail("an exception should have been thrown");
1223         } catch (OrekitException oe) {
1224             Assertions.assertEquals(OrekitMessages.UNABLE_TO_FIND_FILE, oe.getSpecifier());
1225             Assertions.assertEquals(wrongName, oe.getParts()[0]);
1226         }
1227     }
1228 
1229     @Test
1230     public void testMissingTwoSpacecraftFrames() {
1231         final String name = "/ccsds/adm/apm/APM-two-spacecraft-frames.txt";
1232         try {
1233             final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
1234             new ParserBuilder().
1235             withMissionReferenceDate(AbsoluteDate.J2000_EPOCH).
1236             buildApmParser().
1237             parseMessage(source);
1238             Assertions.fail("an exception should have been thrown");
1239         } catch (OrekitException oe) {
1240             Assertions.assertEquals(OrekitMessages.CCSDS_INVALID_FRAME, oe.getSpecifier());
1241             Assertions.assertEquals("INSTRUMENT_2", oe.getParts()[0]);
1242         }
1243     }
1244 
1245     @Test
1246     public void testNoSpacecraftFrames() {
1247         final String name = "/ccsds/adm/apm/APM-no-spacecraft-frames.txt";
1248         try {
1249             final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
1250             new ParserBuilder().
1251             withMissionReferenceDate(AbsoluteDate.J2000_EPOCH).
1252             buildApmParser().
1253             parseMessage(source);
1254             Assertions.fail("an exception should have been thrown");
1255         } catch (OrekitException oe) {
1256             Assertions.assertEquals(OrekitMessages.CCSDS_INVALID_FRAME, oe.getSpecifier());
1257             Assertions.assertEquals("EME2000", oe.getParts()[0]);
1258         }
1259     }
1260 
1261     @Test
1262     public void testMissingFrame() {
1263         final String name = "/ccsds/adm/apm/APM-missing-frame.txt";
1264         try {
1265             final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
1266             new ParserBuilder().
1267             withMissionReferenceDate(AbsoluteDate.J2000_EPOCH).
1268             buildApmParser().
1269             parseMessage(source);
1270             Assertions.fail("an exception should have been thrown");
1271         } catch (OrekitException oe) {
1272             Assertions.assertEquals(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, oe.getSpecifier());
1273             Assertions.assertEquals("Q_FRAME_A", oe.getParts()[0]);
1274         }
1275     }
1276 
1277     @Test
1278     public void testMissingQuaternionComponent() {
1279         final String name = "/ccsds/adm/apm/APM-missing-quaternion-component.txt";
1280         try {
1281             final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
1282             new ParserBuilder().
1283             withMissionReferenceDate(AbsoluteDate.J2000_EPOCH).
1284             buildApmParser().
1285             parseMessage(source);
1286             Assertions.fail("an exception should have been thrown");
1287         } catch (OrekitException oe) {
1288             Assertions.assertEquals(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, oe.getSpecifier());
1289             Assertions.assertEquals("Q{C|1|2|3}", oe.getParts()[0]);
1290         }
1291     }
1292 
1293     @Test
1294     public void testWrongKeyword() {
1295         final String name = "/ccsds/adm/apm/APM-wrong-keyword.txt";
1296         try {
1297             final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
1298             new ParserBuilder().
1299             withMissionReferenceDate(AbsoluteDate.J2000_EPOCH).
1300             buildApmParser().
1301             parseMessage(source);
1302             Assertions.fail("an exception should have been thrown");
1303         } catch (OrekitException oe) {
1304             Assertions.assertEquals(OrekitMessages.CCSDS_UNEXPECTED_KEYWORD, oe.getSpecifier());
1305             Assertions.assertEquals(12, ((Integer) oe.getParts()[0]).intValue());
1306             Assertions.assertTrue(((String) oe.getParts()[2]).startsWith("WRONG_KEYWORD"));
1307         }
1308     }
1309 
1310     @Test
1311     public void testWrongEulerSequence() {
1312         final String name = "/ccsds/adm/apm/APM-wrong-Euler-sequence.txt";
1313         try {
1314             final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
1315             new ParserBuilder().
1316             withMissionReferenceDate(AbsoluteDate.J2000_EPOCH).
1317             buildApmParser().
1318             parseMessage(source);
1319             Assertions.fail("an exception should have been thrown");
1320         } catch (OrekitException oe) {
1321             Assertions.assertEquals(OrekitMessages.CCSDS_INVALID_ROTATION_SEQUENCE, oe.getSpecifier());
1322             Assertions.assertEquals("331", oe.getParts()[0]);
1323             Assertions.assertEquals(33, ((Integer) oe.getParts()[1]).intValue());
1324             Assertions.assertEquals(name, oe.getParts()[2]);
1325         }
1326     }
1327 
1328     @Test
1329     public void testMissingEulerSequence() {
1330         final String name = "/ccsds/adm/apm/APM-missing-Euler-sequence.txt";
1331         try {
1332             final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
1333             new ParserBuilder().
1334             withMissionReferenceDate(AbsoluteDate.J2000_EPOCH).
1335             buildApmParser().
1336             parseMessage(source);
1337             Assertions.fail("an exception should have been thrown");
1338         } catch (OrekitException oe) {
1339             Assertions.assertEquals(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, oe.getSpecifier());
1340             Assertions.assertEquals("EULER_ROT_SEQ", oe.getParts()[0]);
1341         }
1342     }
1343 
1344     @Test
1345     public void testRepeatedEulerAngle() {
1346         final String name = "/ccsds/adm/apm/APM-repeated-Euler-angle.txt";
1347         try {
1348             final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
1349             new ParserBuilder().
1350             withMissionReferenceDate(AbsoluteDate.J2000_EPOCH).
1351             buildApmParser().
1352             parseMessage(source);
1353             Assertions.fail("an exception should have been thrown");
1354         } catch (OrekitException oe) {
1355             Assertions.assertEquals(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, oe.getSpecifier());
1356             Assertions.assertEquals("{X|Y|Z}_ANGLE", oe.getParts()[0]);
1357         }
1358     }
1359 
1360     @Test
1361     public void testSpuriousMetaDataSection() {
1362         final String name = "/ccsds/adm/apm/spurious-metadata.xml";
1363         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
1364         try {
1365             new ParserBuilder().buildApmParser().parseMessage(source);
1366             Assertions.fail("an exception should have been thrown");
1367         } catch (OrekitException oe) {
1368             Assertions.assertEquals(OrekitMessages.CCSDS_UNEXPECTED_KEYWORD, oe.getSpecifier());
1369             Assertions.assertEquals(15, ((Integer) oe.getParts()[0]).intValue());
1370             Assertions.assertEquals("metadata", oe.getParts()[2]);
1371         }
1372     }
1373 
1374 }