1   /* Copyright 2002-2022 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.Assert;
30  import org.junit.Before;
31  import org.junit.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.ndm.ParserBuilder;
44  import org.orekit.files.ccsds.ndm.WriterBuilder;
45  import org.orekit.files.ccsds.ndm.adm.AdmMetadata;
46  import org.orekit.files.ccsds.section.Segment;
47  import org.orekit.files.ccsds.utils.generation.Generator;
48  import org.orekit.files.ccsds.utils.generation.KvnGenerator;
49  import org.orekit.frames.FramesFactory;
50  import org.orekit.time.AbsoluteDate;
51  import org.orekit.time.TimeScalesFactory;
52  import org.orekit.utils.IERSConventions;
53  import org.orekit.utils.PVCoordinates;
54  import org.orekit.utils.PVCoordinatesProvider;
55  import org.orekit.utils.TimeStampedPVCoordinates;
56  
57  public class APMParserTest {
58  
59      private static final double QUATERNION_PRECISION = 1e-5;
60      private static final double ANGLE_PRECISION = 1e-4;
61      private static final double SPACECRAFT_PRECISION = 0.1;
62      private static final double MANEUVER_PRECISION = 1.0e-2;
63  
64      @Before
65      public void setUp() {
66          Utils.setDataRoot("regular-data");
67      }
68  
69      @Test
70      public void testParseAPM1() {
71  
72          // File
73          final String ex = "/ccsds/adm/apm/APMExample1.txt";
74  
75          // Initialize the parser
76          final ApmParser parser = new ParserBuilder().
77                                   withMissionReferenceDate(new AbsoluteDate("2002-09-30T14:28:15.117",
78                                                                             TimeScalesFactory.getUTC())).
79                                   buildApmParser();
80  
81          final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
82  
83          // Generated APM file
84          final Apm file = parser.parseMessage(source);
85  
86          // Verify general data
87          Assert.assertEquals(IERSConventions.IERS_2010, file.getConventions());
88          Assert.assertEquals(DataContext.getDefault(),  file.getDataContext());
89  
90          // Check Header Block
91          Assert.assertEquals(1.0, file.getHeader().getFormatVersion(), 1.0e-10);
92          Assert.assertEquals(new AbsoluteDate(2003, 9, 30, 19, 23, 57,
93                                               TimeScalesFactory.getUTC()),
94                              file.getHeader().getCreationDate());
95          Assert.assertEquals("GSFC", file.getHeader().getOriginator());
96  
97          // Check Metadata Block
98          Assert.assertEquals("TRMM",       file.getMetadata().getObjectName());
99          Assert.assertEquals("1997-009A",  file.getMetadata().getObjectID());
100         Assert.assertEquals(1997,         file.getMetadata().getLaunchYear());
101         Assert.assertEquals(9,            file.getMetadata().getLaunchNumber());
102         Assert.assertEquals("A",          file.getMetadata().getLaunchPiece());
103         Assert.assertEquals("EARTH",      file.getMetadata().getCenter().getName());
104         Assert.assertTrue(file.getMetadata().getHasCreatableBody());
105         Assert.assertEquals(CelestialBodyFactory.getEarth(), file.getMetadata().getCenter().getBody());
106         Assert.assertEquals("UTC",        file.getMetadata().getTimeSystem().name());
107 
108         // Check data block
109         Assert.assertFalse(file.getData().hasManeuvers());
110         Assert.assertEquals(SpacecraftBodyFrame.BaseEquipment.SC_BODY,
111                             file.getData().getQuaternionBlock().getEndpoints().getFrameA().asSpacecraftBodyFrame().getBaseEquipment());
112         Assert.assertEquals("1", file.getData().getQuaternionBlock().getEndpoints().getFrameA().asSpacecraftBodyFrame().getLabel());
113         Assert.assertEquals(CelestialBodyFrame.ITRF1997, file.getData().getQuaternionBlock().getEndpoints().getFrameB().asCelestialBodyFrame());
114         Assert.assertTrue(file.getData().getQuaternionBlock().getEndpoints().isA2b());
115         Assert.assertEquals(new AbsoluteDate(2003, 9, 30, 14, 28, 15.1172,
116                                              TimeScalesFactory.getUTC()),
117                             file.getData().getQuaternionBlock().getEpoch());
118         Assert.assertEquals(0.25678, file.getData().getQuaternionBlock().getQuaternion().getQ0(),    QUATERNION_PRECISION);
119         Assert.assertEquals(0.00005, file.getData().getQuaternionBlock().getQuaternion().getQ1(),    QUATERNION_PRECISION);
120         Assert.assertEquals(0.87543, file.getData().getQuaternionBlock().getQuaternion().getQ2(),    QUATERNION_PRECISION);
121         Assert.assertEquals(0.40949, file.getData().getQuaternionBlock().getQuaternion().getQ3(),    QUATERNION_PRECISION);
122         Assert.assertFalse(file.getData().getQuaternionBlock().hasRates());
123         Assert.assertTrue(Double.isNaN(file.getData().getQuaternionBlock().getQuaternionDot().getQ1()));
124         Assert.assertEquals(new AbsoluteDate(2003, 9, 30, 14, 28, 15.1172,
125                                              TimeScalesFactory.getUTC()),
126                             file.getData().getQuaternionBlock().getAttitude(null, null).getDate());
127         Assert.assertEquals(0.0, file.getData().getQuaternionBlock().getAttitude(null, null).getSpin().getNorm(), 1.0e-15);
128 
129         Attitude attitude = file.getAttitude(null, null);
130         Assert.assertEquals(new AbsoluteDate(2003, 9, 30, 14, 28, 15.1172, TimeScalesFactory.getUTC()),
131                             attitude.getDate());
132         Assert.assertEquals("ITRF-1997/CIO/2010-based ITRF simple EOP", attitude.getReferenceFrame().getName());
133         Assert.assertEquals(2 * FastMath.atan(FastMath.sqrt(0.00005 * 0.00005 + 0.87543 * 0.87543 + 0.40949 * 0.40949) / 0.25678),
134                             attitude.getRotation().getAngle(), 1.0e-15);
135         Assert.assertEquals(0, attitude.getSpin().getNorm(), 1.0e-15);
136 
137     }
138 
139     @Test
140     public void testParseAPM2KVN() {
141         final ApmParser parser = new ParserBuilder().
142                                  withMissionReferenceDate(new AbsoluteDate("2002-09-30T14:28:15.117",
143                                                                            TimeScalesFactory.getUTC())).
144                                  buildApmParser();
145         final String name = "/ccsds/adm/apm/APMExample2.txt";
146         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
147         validateAPM2(parser.parseMessage(source));
148     }
149 
150     @Test
151     public void testParseAPM2XML() {
152         final ApmParser parser = new ParserBuilder().
153                         withMissionReferenceDate(new AbsoluteDate("2002-09-30T14:28:15.117",
154                                                                   TimeScalesFactory.getUTC())).
155                         buildApmParser();
156         final String name = "/ccsds/adm/apm/APMExample2.xml";
157         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
158         validateAPM2(parser.parseMessage(source));
159     }
160 
161     @Test
162     public void testWriteApm2() throws IOException {
163         final ApmParser parser = new ParserBuilder().
164                         withMissionReferenceDate(new AbsoluteDate("2002-09-30T14:28:15.117",
165                                                                   TimeScalesFactory.getUTC())).
166                         buildApmParser();
167         final String name = "/ccsds/adm/apm/APMExample2.xml";
168         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
169         final Apm original = parser.parseMessage(source);
170 
171         // write the parsed file back to a characters array
172         final CharArrayWriter caw = new CharArrayWriter();
173         final Generator generator = new KvnGenerator(caw, ApmWriter.KVN_PADDING_WIDTH, "dummy", 60);
174         new WriterBuilder().buildApmWriter().writeMessage(generator, original);
175 
176         // reparse the written file
177         final byte[]     bytes   = caw.toString().getBytes(StandardCharsets.UTF_8);
178         final DataSource source2 = new DataSource(name, () -> new ByteArrayInputStream(bytes));
179         final Apm    rebuilt = new ParserBuilder().buildApmParser().parseMessage(source2);
180         validateAPM2(rebuilt);
181     }
182 
183     private void validateAPM2(final Apm file) {
184 
185         // Verify general data
186         Assert.assertEquals(IERSConventions.IERS_2010, file.getConventions());
187         Assert.assertEquals(DataContext.getDefault(),  file.getDataContext());
188 
189         // Check Header Block
190         Assert.assertEquals(1.0, file.getHeader().getFormatVersion(), 1.0e-10);
191         Assert.assertEquals(new AbsoluteDate(2004, 2, 14, 19, 23, 57,
192                                              TimeScalesFactory.getUTC()),
193                             file.getHeader().getCreationDate());
194         Assert.assertEquals("JPL", file.getHeader().getOriginator());
195 
196         Segment<AdmMetadata, ApmData> segment = file.getSegments().get(0);
197 
198         // Check Metadata Block
199         Assert.assertEquals("MARS SPIRIT", segment.getMetadata().getObjectName());
200         Assert.assertEquals("2004-003A",   segment.getMetadata().getObjectID());
201         Assert.assertEquals(2004,          segment.getMetadata().getLaunchYear());
202         Assert.assertEquals(3,             segment.getMetadata().getLaunchNumber());
203         Assert.assertEquals("A",           segment.getMetadata().getLaunchPiece());
204         Assert.assertEquals("EARTH",       segment.getMetadata().getCenter().getName());
205         Assert.assertTrue(segment.getMetadata().getHasCreatableBody());
206         Assert.assertTrue(segment.getMetadata().getComments().isEmpty());
207         Assert.assertEquals(CelestialBodyFactory.getEarth(), segment.getMetadata().getCenter().getBody());
208         Assert.assertEquals("UTC",         segment.getMetadata().getTimeSystem().name());
209 
210         // Check general comments
211         ArrayList<String> generalComment = new ArrayList<String>();
212         generalComment.add("GEOCENTRIC, CARTESIAN, EARTH FIXED");
213         generalComment.add("OBJECT ID: 2004-003");
214         generalComment.add("$ITIM = 2004 JAN 14 22:26:18.400000, original launch time 14:36");
215         generalComment.add("Generated by JPL");
216         generalComment.add("Current attitude for orbit 20 and attitude maneuver");
217         generalComment.add("planning data.");
218         generalComment.add("Attitude state quaternion");
219         Assert.assertEquals(generalComment, segment.getData().getComments());
220 
221         // Check data block: QUATERNION
222         Assert.assertEquals(SpacecraftBodyFrame.BaseEquipment.INSTRUMENT,
223                             segment.getData().getQuaternionBlock().getEndpoints().getFrameA().asSpacecraftBodyFrame().getBaseEquipment());
224         Assert.assertEquals("A", segment.getData().getQuaternionBlock().getEndpoints().getFrameA().asSpacecraftBodyFrame().getLabel());
225         Assert.assertEquals(CelestialBodyFrame.ITRF1997, segment.getData().getQuaternionBlock().getEndpoints().getFrameB().asCelestialBodyFrame());
226         Assert.assertEquals(new AbsoluteDate(2004, 2, 14, 14, 28, 15.1172,
227                                              TimeScalesFactory.getUTC()),
228                             segment.getData().getQuaternionBlock().getEpoch());
229         
230         Assert.assertEquals(0.47832, segment.getData().getQuaternionBlock().getQuaternion().getQ0(),    QUATERNION_PRECISION);
231         Assert.assertEquals(0.03123, segment.getData().getQuaternionBlock().getQuaternion().getQ1(),    QUATERNION_PRECISION);
232         Assert.assertEquals(0.78543, segment.getData().getQuaternionBlock().getQuaternion().getQ2(),    QUATERNION_PRECISION);
233         Assert.assertEquals(0.39158, segment.getData().getQuaternionBlock().getQuaternion().getQ3(),    QUATERNION_PRECISION);
234         Assert.assertFalse(segment.getData().getQuaternionBlock().hasRates());
235         Assert.assertTrue(Double.isNaN(segment.getData().getQuaternionBlock().getQuaternionDot().getQ1()));
236 
237         // Check data block: EULER
238         ArrayList<String> eulerComment = new ArrayList<String>();
239         eulerComment.add("Attitude specified as Euler elements");
240         Assert.assertEquals(eulerComment,    segment.getData().getEulerBlock().getComments());
241         Assert.assertEquals(CelestialBodyFrame.ITRF1997, segment.getData().getEulerBlock().getEndpoints().getFrameA().asCelestialBodyFrame());
242         Assert.assertEquals(SpacecraftBodyFrame.BaseEquipment.INSTRUMENT,  segment.getData().getEulerBlock().getEndpoints().getFrameB().asSpacecraftBodyFrame().getBaseEquipment());
243         Assert.assertEquals("A",  segment.getData().getEulerBlock().getEndpoints().getFrameB().asSpacecraftBodyFrame().getLabel());
244         Assert.assertTrue(segment.getData().getEulerBlock().getEndpoints().isA2b());
245         Assert.assertTrue(segment.getData().getEulerBlock().rateFrameIsA());
246         Assert.assertEquals(RotationOrder.ZXY, segment.getData().getEulerBlock().getEulerRotSeq());
247 
248         Assert.assertEquals(-53.3688,  FastMath.toDegrees(segment.getData().getEulerBlock().getRotationAngles()[0]), ANGLE_PRECISION);
249         Assert.assertEquals(139.7527,  FastMath.toDegrees(segment.getData().getEulerBlock().getRotationAngles()[1]), ANGLE_PRECISION);
250         Assert.assertEquals( 25.0658,  FastMath.toDegrees(segment.getData().getEulerBlock().getRotationAngles()[2]), ANGLE_PRECISION);
251         Assert.assertEquals(  0.02156, FastMath.toDegrees(segment.getData().getEulerBlock().getRotationRates()[0]),  ANGLE_PRECISION);
252         Assert.assertEquals(  0.1045,  FastMath.toDegrees(segment.getData().getEulerBlock().getRotationRates()[1]),  ANGLE_PRECISION);
253         Assert.assertEquals(  0.03214, FastMath.toDegrees(segment.getData().getEulerBlock().getRotationRates()[2]),  ANGLE_PRECISION);
254 
255         // Check data block: SPACECRAFT PARAMETERS
256         ArrayList<String> spacecraftComment = new ArrayList<String>();
257         spacecraftComment.add("Spacecraft Parameters");
258         Assert.assertEquals(spacecraftComment, segment.getData().getSpacecraftParametersBlock().getComments());
259         Assert.assertEquals(6080.0,            segment.getData().getSpacecraftParametersBlock().getI11(), SPACECRAFT_PRECISION);
260         Assert.assertEquals(5245.5,            segment.getData().getSpacecraftParametersBlock().getI22(), SPACECRAFT_PRECISION);
261         Assert.assertEquals(8067.3,            segment.getData().getSpacecraftParametersBlock().getI33(), SPACECRAFT_PRECISION);
262         Assert.assertEquals(-135.9,            segment.getData().getSpacecraftParametersBlock().getI12(), SPACECRAFT_PRECISION);
263         Assert.assertEquals(89.3,              segment.getData().getSpacecraftParametersBlock().getI13(), SPACECRAFT_PRECISION);
264         Assert.assertEquals(-90.7,             segment.getData().getSpacecraftParametersBlock().getI23(), SPACECRAFT_PRECISION);
265 
266         // Check data block: MANEUVER
267         ArrayList<String> maneuverComment = new ArrayList<String>();
268         maneuverComment.add("Data follows for 1 planned maneuver.");
269         maneuverComment.add("First attitude maneuver for: MARS SPIRIT");
270         maneuverComment.add("Impulsive, torque direction fixed in body frame");
271         Assert.assertEquals(maneuverComment, segment.getData().getManeuver(0).getComments());
272         Assert.assertTrue(segment.getData().hasManeuvers());
273         Assert.assertEquals(1, segment.getData().getNbManeuvers());
274         Assert.assertEquals(1, segment.getData().getManeuvers().size());
275         Assert.assertEquals("INSTRUMENT A", segment.getData().getManeuver(0).getRefFrameString());
276         Assert.assertEquals(new AbsoluteDate(2004, 2, 14, 14, 29, 0.5098,
277                                              TimeScalesFactory.getUTC()),
278                             segment.getData().getManeuver(0).getEpochStart());
279         Assert.assertEquals(3,     segment.getData().getManeuver(0).getDuration(),      MANEUVER_PRECISION);
280         Assert.assertEquals(-1.25, segment.getData().getManeuver(0).getTorque().getX(), MANEUVER_PRECISION);
281         Assert.assertEquals(-0.5,  segment.getData().getManeuver(0).getTorque().getY(), MANEUVER_PRECISION);
282         Assert.assertEquals(0.5,   segment.getData().getManeuver(0).getTorque().getZ(), MANEUVER_PRECISION);
283 
284     }
285 
286     @Test
287     public void testParseAPM3() {
288 
289         // File
290         final String ex = "/ccsds/adm/apm/APMExample3.txt";
291 
292         // Initialize the parser
293         final ApmParser parser = new ParserBuilder().
294                                  withMissionReferenceDate(new AbsoluteDate("2002-09-30T14:28:15.117",
295                                                                            TimeScalesFactory.getUTC())).
296                                  buildApmParser();
297 
298         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
299 
300         // Generated APM file
301         final Apm file = parser.parseMessage(source);
302 
303         // Verify general data
304         Assert.assertEquals(IERSConventions.IERS_2010, file.getConventions());
305         Assert.assertEquals(DataContext.getDefault(),  file.getDataContext());
306 
307         // Check Header Block
308         Assert.assertEquals(1.0, file.getHeader().getFormatVersion(), 1.0e-10);
309         Assert.assertEquals(new AbsoluteDate(2003, 9, 30, 19, 23, 57,
310                                              TimeScalesFactory.getUTC()),
311                             file.getHeader().getCreationDate());
312         Assert.assertEquals("GSFC", file.getHeader().getOriginator());
313 
314         Segment<AdmMetadata, ApmData> segment = file.getSegments().get(0);
315 
316         // Check Metadata Block
317         Assert.assertEquals("TRMM",       segment.getMetadata().getObjectName());
318         Assert.assertEquals("1997-009A",  segment.getMetadata().getObjectID());
319         Assert.assertEquals(1997,         segment.getMetadata().getLaunchYear());
320         Assert.assertEquals(9,            segment.getMetadata().getLaunchNumber());
321         Assert.assertEquals("A",          segment.getMetadata().getLaunchPiece());
322         Assert.assertEquals("EARTH",      segment.getMetadata().getCenter().getName());
323         Assert.assertTrue(segment.getMetadata().getHasCreatableBody());
324         Assert.assertEquals(CelestialBodyFactory.getEarth(), segment.getMetadata().getCenter().getBody());
325         Assert.assertEquals("UTC",        segment.getMetadata().getTimeSystem().name());
326 
327         // Check data block: QUATERNION
328         Assert.assertFalse(segment.getData().hasManeuvers());
329         Assert.assertEquals(SpacecraftBodyFrame.BaseEquipment.SC_BODY,
330                             segment.getData().getQuaternionBlock().getEndpoints().getFrameA().asSpacecraftBodyFrame().getBaseEquipment());
331         Assert.assertEquals("1", segment.getData().getQuaternionBlock().getEndpoints().getFrameA().asSpacecraftBodyFrame().getLabel());
332         Assert.assertEquals(CelestialBodyFrame.ITRF1997, segment.getData().getQuaternionBlock().getEndpoints().getFrameB().asCelestialBodyFrame());
333         Assert.assertFalse(segment.getData().getQuaternionBlock().getEndpoints().isA2b());
334         Assert.assertEquals(new AbsoluteDate(2003, 9, 30, 14, 28, 15.1172,
335                                              TimeScalesFactory.getUTC()),
336                             segment.getData().getQuaternionBlock().getEpoch());
337         Assert.assertEquals(0.25678, segment.getData().getQuaternionBlock().getQuaternion().getQ0(),    QUATERNION_PRECISION);
338         Assert.assertEquals(0.00005, segment.getData().getQuaternionBlock().getQuaternion().getQ1(),    QUATERNION_PRECISION);
339         Assert.assertEquals(0.87543, segment.getData().getQuaternionBlock().getQuaternion().getQ2(),    QUATERNION_PRECISION);
340         Assert.assertEquals(0.40949, segment.getData().getQuaternionBlock().getQuaternion().getQ3(),    QUATERNION_PRECISION);
341         Assert.assertFalse(segment.getData().getQuaternionBlock().hasRates());
342         Assert.assertTrue(Double.isNaN(segment.getData().getQuaternionBlock().getQuaternionDot().getQ1()));
343 
344         // Check data block: SPIN
345         ArrayList<String> spinComment = new ArrayList<String>();
346         spinComment.add("SPIN Parameters");
347         Assert.assertEquals(spinComment, segment.getData().getSpinStabilizedBlock().getComments());
348         Assert.assertEquals(SpacecraftBodyFrame.BaseEquipment.SC_BODY,
349                             segment.getData().getSpinStabilizedBlock().getEndpoints().getFrameB().asSpacecraftBodyFrame().getBaseEquipment());
350         Assert.assertEquals("1", segment.getData().getSpinStabilizedBlock().getEndpoints().getFrameB().asSpacecraftBodyFrame().getLabel());
351         Assert.assertEquals(CelestialBodyFrame.ITRF1997, segment.getData().getSpinStabilizedBlock().getEndpoints().getFrameA().asCelestialBodyFrame());
352         Assert.assertFalse(segment.getData().getSpinStabilizedBlock().getEndpoints().isA2b());
353         Assert.assertEquals(FastMath.toRadians(24.8),   segment.getData().getSpinStabilizedBlock().getSpinAlpha(),      ANGLE_PRECISION);
354         Assert.assertEquals(FastMath.toRadians(33.7),   segment.getData().getSpinStabilizedBlock().getSpinDelta(),      ANGLE_PRECISION);
355         Assert.assertEquals(FastMath.toRadians(42.5),   segment.getData().getSpinStabilizedBlock().getSpinAngle(),      ANGLE_PRECISION);
356         Assert.assertEquals(FastMath.toRadians(-135.9), segment.getData().getSpinStabilizedBlock().getSpinAngleVel(),   ANGLE_PRECISION);
357         Assert.assertEquals(FastMath.toRadians(89.3),   segment.getData().getSpinStabilizedBlock().getNutation(),       ANGLE_PRECISION);
358         Assert.assertEquals(FastMath.toRadians(-90.7),  segment.getData().getSpinStabilizedBlock().getNutationPhase(),  ANGLE_PRECISION);
359         Assert.assertEquals(64.0,                       segment.getData().getSpinStabilizedBlock().getNutationPeriod(), ANGLE_PRECISION);
360 
361     }
362 
363     @Test
364     public void testParseAPM4() {
365 
366         // File
367         final String ex = "/ccsds/adm/apm/APMExample4.txt";
368 
369         // Initialize the parser
370         final ApmParser parser = new ParserBuilder().
371                                  withMissionReferenceDate(new AbsoluteDate("2002-09-30T14:28:15.117",
372                                                                            TimeScalesFactory.getUTC())).
373                                  buildApmParser();
374 
375         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
376 
377         // Generated APM file
378         final Apm file = parser.parseMessage(source);
379 
380         // Verify general data
381         Assert.assertEquals(IERSConventions.IERS_2010, file.getConventions());
382         Assert.assertEquals(DataContext.getDefault(),  file.getDataContext());
383 
384         // Check Header Block
385         Assert.assertEquals(1.0, file.getHeader().getFormatVersion(), 1.0e-10);
386         Assert.assertEquals(new AbsoluteDate(2003, 9, 30, 19, 23, 57,
387                                              TimeScalesFactory.getUTC()),
388                             file.getHeader().getCreationDate());
389         Assert.assertEquals("GSFC", file.getHeader().getOriginator());
390 
391         Segment<AdmMetadata, ApmData> segment = file.getSegments().get(0);
392 
393         // Check Metadata Block
394         Assert.assertEquals("TRMM",       segment.getMetadata().getObjectName());
395         Assert.assertEquals("1997-009A",  segment.getMetadata().getObjectID());
396         Assert.assertEquals(1997,         segment.getMetadata().getLaunchYear());
397         Assert.assertEquals(9,            segment.getMetadata().getLaunchNumber());
398         Assert.assertEquals("A",          segment.getMetadata().getLaunchPiece());
399         Assert.assertEquals("EARTH",      segment.getMetadata().getCenter().getName());
400         Assert.assertTrue(segment.getMetadata().getHasCreatableBody());
401         Assert.assertEquals(CelestialBodyFactory.getEarth(), segment.getMetadata().getCenter().getBody());
402         Assert.assertEquals("UTC",        segment.getMetadata().getTimeSystem().name());
403 
404         // Check data block
405         Assert.assertFalse(segment.getData().hasManeuvers());
406         Assert.assertEquals(SpacecraftBodyFrame.BaseEquipment.SC_BODY,
407                             segment.getData().getQuaternionBlock().getEndpoints().getFrameA().asSpacecraftBodyFrame().getBaseEquipment());
408         Assert.assertEquals("1", segment.getData().getQuaternionBlock().getEndpoints().getFrameA().asSpacecraftBodyFrame().getLabel());
409         Assert.assertEquals(CelestialBodyFrame.ITRF1997, segment.getData().getQuaternionBlock().getEndpoints().getFrameB().asCelestialBodyFrame());
410         Assert.assertTrue(segment.getData().getQuaternionBlock().getEndpoints().isA2b());
411         Assert.assertEquals(new AbsoluteDate(2003, 9, 30, 14, 28, 15.1172,
412                                              TimeScalesFactory.getUTC()),
413                             segment.getData().getQuaternionBlock().getEpoch());
414         Assert.assertEquals(0.25678, segment.getData().getQuaternionBlock().getQuaternion().getQ0(),    QUATERNION_PRECISION);
415         Assert.assertEquals(0.00005, segment.getData().getQuaternionBlock().getQuaternion().getQ1(),    QUATERNION_PRECISION);
416         Assert.assertEquals(0.87543, segment.getData().getQuaternionBlock().getQuaternion().getQ2(),    QUATERNION_PRECISION);
417         Assert.assertEquals(0.40949, segment.getData().getQuaternionBlock().getQuaternion().getQ3(),    QUATERNION_PRECISION);
418         Assert.assertEquals(0.05678, segment.getData().getQuaternionBlock().getQuaternionDot().getQ0(), QUATERNION_PRECISION);
419         Assert.assertEquals(0.00001, segment.getData().getQuaternionBlock().getQuaternionDot().getQ1(), QUATERNION_PRECISION);
420         Assert.assertEquals(0.07543, segment.getData().getQuaternionBlock().getQuaternionDot().getQ2(), QUATERNION_PRECISION);
421         Assert.assertEquals(0.00949, segment.getData().getQuaternionBlock().getQuaternionDot().getQ3(), QUATERNION_PRECISION);
422         Assert.assertEquals(new AbsoluteDate(2003, 9, 30, 14, 28, 15.1172,
423                                              TimeScalesFactory.getUTC()),
424                             segment.getData().getQuaternionBlock().getAttitude(null, null).getDate());
425         Assert.assertEquals(8.63363e-2,
426                             segment.getData().getQuaternionBlock().getAttitude(null, null).getSpin().getNorm(),
427                             1.0e-7);
428 
429         Attitude attitude = file.getAttitude(null, null);
430         Assert.assertEquals(segment.getData().getQuaternionBlock().getEpoch(),
431                             attitude.getDate());
432         Assert.assertEquals(8.63363e-2, attitude.getSpin().getNorm(), 1.0e-7);
433 
434     }
435 
436     @Test
437     public void testParseAPM5() {
438 
439         // File
440         final String ex = "/ccsds/adm/apm/APMExample5.txt";
441 
442         // Initialize the parser
443         final ApmParser parser = new ParserBuilder().buildApmParser();
444 
445         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
446 
447         // Generated APM file
448         final Apm file = parser.parseMessage(source);
449 
450         // Verify general data
451         Assert.assertEquals(IERSConventions.IERS_2010, file.getConventions());
452         Assert.assertEquals(DataContext.getDefault(),  file.getDataContext());
453 
454         // Check Header Block
455         Assert.assertEquals(1.0, file.getHeader().getFormatVersion(), 1.0e-10);
456         Assert.assertEquals(new AbsoluteDate(2004, 2, 14, 19, 23, 57,
457                                              TimeScalesFactory.getUTC()),
458                             file.getHeader().getCreationDate());
459         Assert.assertEquals("GSFC", file.getHeader().getOriginator());
460 
461         Segment<AdmMetadata, ApmData> segment = file.getSegments().get(0);
462 
463         // Check Metadata Block
464         Assert.assertEquals("TRMM",       segment.getMetadata().getObjectName());
465         Assert.assertEquals("1997-009A",  segment.getMetadata().getObjectID());
466         Assert.assertEquals(1997,         segment.getMetadata().getLaunchYear());
467         Assert.assertEquals(9,            segment.getMetadata().getLaunchNumber());
468         Assert.assertEquals("A",          segment.getMetadata().getLaunchPiece());
469         Assert.assertEquals("EARTH",      segment.getMetadata().getCenter().getName());
470         Assert.assertTrue(segment.getMetadata().getHasCreatableBody());
471         Assert.assertEquals(CelestialBodyFactory.getEarth(), segment.getMetadata().getCenter().getBody());
472         Assert.assertEquals("UTC",        segment.getMetadata().getTimeSystem().name());
473 
474         // Check data block
475         Assert.assertFalse(segment.getData().hasManeuvers());
476         Assert.assertEquals(SpacecraftBodyFrame.BaseEquipment.SC_BODY,
477                             segment.getData().getQuaternionBlock().getEndpoints().getFrameA().asSpacecraftBodyFrame().getBaseEquipment());
478         Assert.assertEquals("1", segment.getData().getQuaternionBlock().getEndpoints().getFrameA().asSpacecraftBodyFrame().getLabel());
479         Assert.assertEquals(CelestialBodyFrame.ITRF1997, segment.getData().getQuaternionBlock().getEndpoints().getFrameB().asCelestialBodyFrame());
480         Assert.assertTrue(segment.getData().getQuaternionBlock().getEndpoints().isA2b());
481         Assert.assertEquals(new AbsoluteDate(2004, 2, 14, 14, 28, 15.1172,
482                                              TimeScalesFactory.getUTC()),
483                             segment.getData().getQuaternionBlock().getEpoch());
484         Assert.assertEquals(0.47832, segment.getData().getQuaternionBlock().getQuaternion().getQ0(), QUATERNION_PRECISION);
485         Assert.assertEquals(0.03123, segment.getData().getQuaternionBlock().getQuaternion().getQ1(), QUATERNION_PRECISION);
486         Assert.assertEquals(0.78543, segment.getData().getQuaternionBlock().getQuaternion().getQ2(), QUATERNION_PRECISION);
487         Assert.assertEquals(0.39158, segment.getData().getQuaternionBlock().getQuaternion().getQ3(), QUATERNION_PRECISION);
488         Assert.assertTrue(Double.isNaN(segment.getData().getQuaternionBlock().getQuaternionDot().getQ0()));
489         Assert.assertTrue(Double.isNaN(segment.getData().getQuaternionBlock().getQuaternionDot().getQ1()));
490         Assert.assertTrue(Double.isNaN(segment.getData().getQuaternionBlock().getQuaternionDot().getQ2()));
491         Assert.assertTrue(Double.isNaN(segment.getData().getQuaternionBlock().getQuaternionDot().getQ3()));
492 
493         Assert.assertEquals(RotationOrder.ZXY, segment.getData().getEulerBlock().getEulerRotSeq());
494         Assert.assertTrue(Double.isNaN(segment.getData().getEulerBlock().getRotationAngles()[0]));
495         Assert.assertTrue(Double.isNaN(segment.getData().getEulerBlock().getRotationAngles()[1]));
496         Assert.assertTrue(Double.isNaN(segment.getData().getEulerBlock().getRotationAngles()[2]));
497         Assert.assertEquals(0.02156, FastMath.toDegrees(segment.getData().getEulerBlock().getRotationRates()[0]), ANGLE_PRECISION);
498         Assert.assertEquals(0.1045,  FastMath.toDegrees(segment.getData().getEulerBlock().getRotationRates()[1]), ANGLE_PRECISION);
499         Assert.assertEquals(0.03214, FastMath.toDegrees(segment.getData().getEulerBlock().getRotationRates()[2]), ANGLE_PRECISION);
500 
501         Attitude attitude = file.getAttitude(null, null);
502         Assert.assertEquals(segment.getData().getQuaternionBlock().getEpoch(),
503                             attitude.getDate());
504         Assert.assertEquals(1.9449e-3, attitude.getSpin().getNorm(), 1.0e-7);
505 
506     }
507 
508     @Test
509     public void testParseAPM6() {
510 
511         // File
512         final String ex = "/ccsds/adm/apm/APMExample6.txt";
513 
514         // Initialize the parser
515         final ApmParser parser = new ParserBuilder().buildApmParser();
516 
517         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
518 
519         // Generated APM file
520         final Apm file = parser.parseMessage(source);
521 
522         // Verify general data
523         Assert.assertEquals(IERSConventions.IERS_2010, file.getConventions());
524         Assert.assertEquals(DataContext.getDefault(),  file.getDataContext());
525 
526         // Check Header Block
527         Assert.assertEquals(1.0, file.getHeader().getFormatVersion(), 1.0e-10);
528         Assert.assertEquals(new AbsoluteDate(2004, 2, 14, 19, 23, 57,
529                                              TimeScalesFactory.getUTC()),
530                             file.getHeader().getCreationDate());
531         Assert.assertEquals("GSFC", file.getHeader().getOriginator());
532 
533         Segment<AdmMetadata, ApmData> segment = file.getSegments().get(0);
534 
535         // Check Metadata Block
536         Assert.assertEquals("TRMM",       segment.getMetadata().getObjectName());
537         Assert.assertEquals("1997-009A",  segment.getMetadata().getObjectID());
538         Assert.assertEquals(1997,         segment.getMetadata().getLaunchYear());
539         Assert.assertEquals(9,            segment.getMetadata().getLaunchNumber());
540         Assert.assertEquals("A",          segment.getMetadata().getLaunchPiece());
541         Assert.assertEquals("EARTH",      segment.getMetadata().getCenter().getName());
542         Assert.assertTrue(segment.getMetadata().getHasCreatableBody());
543         Assert.assertEquals(CelestialBodyFactory.getEarth(), segment.getMetadata().getCenter().getBody());
544         Assert.assertEquals("UTC",        segment.getMetadata().getTimeSystem().name());
545 
546         // Check data block
547         Assert.assertFalse(segment.getData().hasManeuvers());
548         Assert.assertEquals(SpacecraftBodyFrame.BaseEquipment.SC_BODY,
549                             segment.getData().getQuaternionBlock().getEndpoints().getFrameA().asSpacecraftBodyFrame().getBaseEquipment());
550         Assert.assertEquals("1", segment.getData().getQuaternionBlock().getEndpoints().getFrameA().asSpacecraftBodyFrame().getLabel());
551         Assert.assertEquals(CelestialBodyFrame.ITRF1997, segment.getData().getQuaternionBlock().getEndpoints().getFrameB().asCelestialBodyFrame());
552         Assert.assertTrue(segment.getData().getQuaternionBlock().getEndpoints().isA2b());
553         Assert.assertEquals(new AbsoluteDate(2004, 2, 14, 14, 28, 15.1172,
554                                              TimeScalesFactory.getUTC()),
555                             segment.getData().getQuaternionBlock().getEpoch());
556         Assert.assertEquals(0.47832, segment.getData().getQuaternionBlock().getQuaternion().getQ0(), QUATERNION_PRECISION);
557         Assert.assertEquals(0.03123, segment.getData().getQuaternionBlock().getQuaternion().getQ1(), QUATERNION_PRECISION);
558         Assert.assertEquals(0.78543, segment.getData().getQuaternionBlock().getQuaternion().getQ2(), QUATERNION_PRECISION);
559         Assert.assertEquals(0.39158, segment.getData().getQuaternionBlock().getQuaternion().getQ3(), QUATERNION_PRECISION);
560         Assert.assertTrue(Double.isNaN(segment.getData().getQuaternionBlock().getQuaternionDot().getQ0()));
561         Assert.assertTrue(Double.isNaN(segment.getData().getQuaternionBlock().getQuaternionDot().getQ1()));
562         Assert.assertTrue(Double.isNaN(segment.getData().getQuaternionBlock().getQuaternionDot().getQ2()));
563         Assert.assertTrue(Double.isNaN(segment.getData().getQuaternionBlock().getQuaternionDot().getQ3()));
564 
565         Assert.assertEquals(RotationOrder.ZXY, segment.getData().getEulerBlock().getEulerRotSeq());
566         Assert.assertEquals(0.02156, FastMath.toDegrees(segment.getData().getEulerBlock().getRotationAngles()[0]), ANGLE_PRECISION);
567         Assert.assertEquals(0.1045,  FastMath.toDegrees(segment.getData().getEulerBlock().getRotationAngles()[1]), ANGLE_PRECISION);
568         Assert.assertEquals(0.03214, FastMath.toDegrees(segment.getData().getEulerBlock().getRotationAngles()[2]), ANGLE_PRECISION);
569         Assert.assertTrue(Double.isNaN(segment.getData().getEulerBlock().getRotationRates()[0]));
570         Assert.assertTrue(Double.isNaN(segment.getData().getEulerBlock().getRotationRates()[1]));
571         Assert.assertTrue(Double.isNaN(segment.getData().getEulerBlock().getRotationRates()[2]));
572 
573         Attitude attitude = file.getAttitude(null, null);
574         Assert.assertEquals(segment.getData().getQuaternionBlock().getEpoch(),
575                             attitude.getDate());
576         Assert.assertEquals(0.0, attitude.getSpin().getNorm(), 1.0e-15);
577 
578     }
579 
580     @Test
581     public void testOrbitRelativeFrameInertial() {
582 
583         // File
584         final String ex = "/ccsds/adm/apm/APM-orbit-relative-frame-inertial.txt";
585 
586         // Initialize the parser
587         final ApmParser parser = new ParserBuilder().
588                                  withMissionReferenceDate(new AbsoluteDate("2002-09-30T14:28:15.117",
589                                                                            TimeScalesFactory.getUTC())).
590                                  buildApmParser();
591 
592         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
593 
594         // Generated APM file
595         final Apm file = parser.parseMessage(source);
596 
597         // Verify general data
598         Assert.assertEquals(IERSConventions.IERS_2010, file.getConventions());
599         Assert.assertEquals(DataContext.getDefault(),  file.getDataContext());
600 
601         // Check Header Block
602         Assert.assertEquals(1.0, file.getHeader().getFormatVersion(), 1.0e-10);
603         Assert.assertEquals(new AbsoluteDate(2021, 2, 24, 18, 59, 43,
604                                              TimeScalesFactory.getUTC()),
605                             file.getHeader().getCreationDate());
606         Assert.assertEquals("CS GROUP", file.getHeader().getOriginator());
607 
608         Segment<AdmMetadata, ApmData> segment = file.getSegments().get(0);
609 
610         // Check Metadata Block
611         Assert.assertEquals("DUMMY",      segment.getMetadata().getObjectName());
612         Assert.assertEquals("9999-111Z",  segment.getMetadata().getObjectID());
613         Assert.assertEquals(9999,         segment.getMetadata().getLaunchYear());
614         Assert.assertEquals(111,          segment.getMetadata().getLaunchNumber());
615         Assert.assertEquals("Z",          segment.getMetadata().getLaunchPiece());
616         Assert.assertEquals("JUPITER",    segment.getMetadata().getCenter().getName());
617         Assert.assertTrue(segment.getMetadata().getHasCreatableBody());
618         Assert.assertEquals(CelestialBodyFactory.getJupiter(), segment.getMetadata().getCenter().getBody());
619         Assert.assertEquals("TDB",        segment.getMetadata().getTimeSystem().name());
620 
621         // Check data block
622         Assert.assertFalse(segment.getData().hasManeuvers());
623         Assert.assertEquals(SpacecraftBodyFrame.BaseEquipment.SC_BODY,
624                             segment.getData().getQuaternionBlock().getEndpoints().getFrameA().asSpacecraftBodyFrame().getBaseEquipment());
625         Assert.assertEquals("1", segment.getData().getQuaternionBlock().getEndpoints().getFrameA().asSpacecraftBodyFrame().getLabel());
626         Assert.assertEquals(OrbitRelativeFrame.VNC_INERTIAL, segment.getData().getQuaternionBlock().getEndpoints().getFrameB().asOrbitRelativeFrame());
627         Assert.assertFalse(segment.getData().getQuaternionBlock().getEndpoints().isA2b());
628         Assert.assertEquals(new AbsoluteDate(2021, 1, 1, 0, 0, 0.0, TimeScalesFactory.getTDB()),
629                             segment.getData().getQuaternionBlock().getEpoch());
630 
631         final PVCoordinates pv = new PVCoordinates(new Vector3D( 1.234e7, -0.567e7, 9.876e6),
632                                                    new Vector3D(-0.772e4,  5.002e4, 4.892e2));
633         Attitude attitude = file.getAttitude(FramesFactory.getEME2000(),
634                                              (date, frame) -> new TimeStampedPVCoordinates(date, pv));
635         Vector3D xSat = attitude.getRotation().applyInverseTo(Vector3D.PLUS_I);
636         Vector3D ySat = attitude.getRotation().applyInverseTo(Vector3D.PLUS_J);
637         Assert.assertEquals(FastMath.PI, Vector3D.angle(xSat, pv.getVelocity()), 1.0e-10);
638         Assert.assertEquals(0.0,         Vector3D.angle(ySat, pv.getMomentum()), 1.0e-10);
639         Assert.assertEquals(0.0,         attitude.getSpin().getX(), 1.0e-10);
640         Assert.assertEquals(0.0,         attitude.getSpin().getY(), 1.0e-10);
641         Assert.assertEquals(0.0,         attitude.getSpin().getZ(), 1.0e-10);
642 
643     }
644 
645     @Test
646     public void testOrbitRelativeFrameRotating() {
647 
648         // File
649         final String ex = "/ccsds/adm/apm/APM-orbit-relative-frame-rotating.txt";
650 
651         // Initialize the parser
652         final ApmParser parser = new ParserBuilder().
653                                  withMissionReferenceDate(new AbsoluteDate("2002-09-30T14:28:15.117",
654                                                                            TimeScalesFactory.getUTC())).
655                                  buildApmParser();
656 
657         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
658 
659         // Generated APM file
660         final Apm file = parser.parseMessage(source);
661 
662         // Verify general data
663         Assert.assertEquals(IERSConventions.IERS_2010, file.getConventions());
664         Assert.assertEquals(DataContext.getDefault(),  file.getDataContext());
665 
666         // Check Header Block
667         Assert.assertEquals(1.0, file.getHeader().getFormatVersion(), 1.0e-10);
668         Assert.assertEquals(new AbsoluteDate(2021, 2, 24, 18, 59, 43,
669                                              TimeScalesFactory.getUTC()),
670                             file.getHeader().getCreationDate());
671         Assert.assertEquals("CS GROUP", file.getHeader().getOriginator());
672 
673         Segment<AdmMetadata, ApmData> segment = file.getSegments().get(0);
674 
675         // Check Metadata Block
676         Assert.assertEquals("DUMMY",      segment.getMetadata().getObjectName());
677         Assert.assertEquals("9999-111Z",  segment.getMetadata().getObjectID());
678         Assert.assertEquals(9999,         segment.getMetadata().getLaunchYear());
679         Assert.assertEquals(111,          segment.getMetadata().getLaunchNumber());
680         Assert.assertEquals("Z",          segment.getMetadata().getLaunchPiece());
681         Assert.assertEquals("JUPITER",    segment.getMetadata().getCenter().getName());
682         Assert.assertTrue(segment.getMetadata().getHasCreatableBody());
683         Assert.assertEquals(CelestialBodyFactory.getJupiter(), segment.getMetadata().getCenter().getBody());
684         Assert.assertEquals("TDB",        segment.getMetadata().getTimeSystem().name());
685 
686         // Check data block
687         Assert.assertFalse(segment.getData().hasManeuvers());
688         Assert.assertEquals(SpacecraftBodyFrame.BaseEquipment.SC_BODY,
689                             segment.getData().getQuaternionBlock().getEndpoints().getFrameA().asSpacecraftBodyFrame().getBaseEquipment());
690         Assert.assertEquals("1", segment.getData().getQuaternionBlock().getEndpoints().getFrameA().asSpacecraftBodyFrame().getLabel());
691         Assert.assertEquals(OrbitRelativeFrame.VNC_ROTATING, segment.getData().getQuaternionBlock().getEndpoints().getFrameB().asOrbitRelativeFrame());
692         Assert.assertFalse(segment.getData().getQuaternionBlock().getEndpoints().isA2b());
693         Assert.assertEquals(new AbsoluteDate(2021, 1, 1, 0, 0, 0.0, TimeScalesFactory.getTDB()),
694                             segment.getData().getQuaternionBlock().getEpoch());
695 
696         PVCoordinates pv = new PVCoordinates(new Vector3D( 1.234e7, -0.567e7, 9.876e6),
697                                              new Vector3D(-0.772e4,  5.002e4, 4.892e2));
698         Attitude attitude = file.getAttitude(FramesFactory.getEME2000(),
699                                              (date, frame) -> new TimeStampedPVCoordinates(date, pv));
700         Vector3D xSat = attitude.getRotation().applyInverseTo(Vector3D.PLUS_I);
701         Vector3D ySat = attitude.getRotation().applyInverseTo(Vector3D.PLUS_J);
702         Assert.assertEquals(FastMath.PI, Vector3D.angle(xSat, pv.getVelocity()), 1.0e-10);
703         Assert.assertEquals(0.0,         Vector3D.angle(ySat, pv.getMomentum()), 1.0e-10);
704         Assert.assertEquals(0.0,                               attitude.getSpin().getX(), 1.0e-10);
705         Assert.assertEquals(pv.getAngularVelocity().getNorm(), attitude.getSpin().getY(), 1.0e-10);
706         Assert.assertEquals(0.0,                               attitude.getSpin().getZ(), 1.0e-10);
707 
708     }
709 
710     @Test
711     public void testUnsupportedOrbitRelativeFrame() {
712 
713         // File
714         final String ex = "/ccsds/adm/apm/APM-unsupported-orbit-relative-frame.txt";
715 
716         // Initialize the parser
717         final ApmParser parser = new ParserBuilder().
718                                  withMissionReferenceDate(new AbsoluteDate("2002-09-30T14:28:15.117",
719                                                                            TimeScalesFactory.getUTC())).
720                                  buildApmParser();
721 
722         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
723 
724         // Generated APM file
725         final Apm file = parser.parseMessage(source);
726 
727         // Verify general data
728         Assert.assertEquals(IERSConventions.IERS_2010, file.getConventions());
729         Assert.assertEquals(DataContext.getDefault(),  file.getDataContext());
730 
731         // Check Header Block
732         Assert.assertEquals(1.0, file.getHeader().getFormatVersion(), 1.0e-10);
733         Assert.assertEquals(new AbsoluteDate(2021, 2, 24, 18, 59, 43,
734                                              TimeScalesFactory.getUTC()),
735                             file.getHeader().getCreationDate());
736         Assert.assertEquals("CS GROUP", file.getHeader().getOriginator());
737 
738         Segment<AdmMetadata, ApmData> segment = file.getSegments().get(0);
739 
740         // Check Metadata Block
741         Assert.assertEquals("DUMMY",      segment.getMetadata().getObjectName());
742         Assert.assertEquals("9999-111Z",  segment.getMetadata().getObjectID());
743         Assert.assertEquals(9999,         segment.getMetadata().getLaunchYear());
744         Assert.assertEquals(111,          segment.getMetadata().getLaunchNumber());
745         Assert.assertEquals("Z",          segment.getMetadata().getLaunchPiece());
746         Assert.assertEquals("JUPITER",    segment.getMetadata().getCenter().getName());
747         Assert.assertTrue(segment.getMetadata().getHasCreatableBody());
748         Assert.assertEquals(CelestialBodyFactory.getJupiter(), segment.getMetadata().getCenter().getBody());
749         Assert.assertEquals("TDB",        segment.getMetadata().getTimeSystem().name());
750 
751         // Check data block
752         Assert.assertFalse(segment.getData().hasManeuvers());
753         Assert.assertEquals(SpacecraftBodyFrame.BaseEquipment.SC_BODY,
754                             segment.getData().getQuaternionBlock().getEndpoints().getFrameA().asSpacecraftBodyFrame().getBaseEquipment());
755         Assert.assertEquals("1", segment.getData().getQuaternionBlock().getEndpoints().getFrameA().asSpacecraftBodyFrame().getLabel());
756         Assert.assertEquals(OrbitRelativeFrame.SEZ_INERTIAL, segment.getData().getQuaternionBlock().getEndpoints().getFrameB().asOrbitRelativeFrame());
757         Assert.assertTrue(segment.getData().getQuaternionBlock().getEndpoints().isA2b());
758         Assert.assertEquals(new AbsoluteDate(2021, 1, 1, 0, 0, 0.0, TimeScalesFactory.getTDB()),
759                             segment.getData().getQuaternionBlock().getEpoch());
760 
761         final PVCoordinatesProvider prov = (date, frame) -> new TimeStampedPVCoordinates(date,
762                                                                                          new PVCoordinates(new Vector3D( 1.234e7, -0.567e7, 9.876e6),
763                                                                                                            new Vector3D(-0.772e4,  5.002e4, 4.892e2)));
764         try {
765             file.getAttitude(FramesFactory.getEME2000(), prov);
766             Assert.fail("an exception should have been thrown");
767         } catch (OrekitException oe) {
768             Assert.assertEquals(OrekitMessages.UNSUPPORTED_LOCAL_ORBITAL_FRAME, oe.getSpecifier());
769             Assert.assertEquals(OrbitRelativeFrame.SEZ_INERTIAL.name(), oe.getParts()[0]);
770         }
771 
772     }
773 
774     @Test
775     public void testUnknownFrame() {
776 
777         // File
778         final String ex = "/ccsds/adm/apm/APM-unknown-frame.txt";
779 
780         // Initialize the parser
781         final ApmParser parser = new ParserBuilder().
782                                  withMissionReferenceDate(new AbsoluteDate("2002-09-30T14:28:15.117",
783                                                                            TimeScalesFactory.getUTC())).
784                                  buildApmParser();
785 
786         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
787 
788         // Generated APM file
789         final Apm file = parser.parseMessage(source);
790 
791         // Verify general data
792         Assert.assertEquals(IERSConventions.IERS_2010, file.getConventions());
793         Assert.assertEquals(DataContext.getDefault(),  file.getDataContext());
794 
795         // Check Header Block
796         Assert.assertEquals(1.0, file.getHeader().getFormatVersion(), 1.0e-10);
797         Assert.assertEquals(new AbsoluteDate(2021, 2, 24, 18, 59, 43,
798                                              TimeScalesFactory.getUTC()),
799                             file.getHeader().getCreationDate());
800         Assert.assertEquals("CS GROUP", file.getHeader().getOriginator());
801 
802         Segment<AdmMetadata, ApmData> segment = file.getSegments().get(0);
803 
804         // Check Metadata Block
805         Assert.assertEquals("DUMMY",      segment.getMetadata().getObjectName());
806         Assert.assertEquals("9999-111Z",  segment.getMetadata().getObjectID());
807         Assert.assertEquals(9999,         segment.getMetadata().getLaunchYear());
808         Assert.assertEquals(111,          segment.getMetadata().getLaunchNumber());
809         Assert.assertEquals("Z",          segment.getMetadata().getLaunchPiece());
810         Assert.assertEquals("JUPITER",    segment.getMetadata().getCenter().getName());
811         Assert.assertTrue(segment.getMetadata().getHasCreatableBody());
812         Assert.assertEquals(CelestialBodyFactory.getJupiter(), segment.getMetadata().getCenter().getBody());
813         Assert.assertEquals("TDB",        segment.getMetadata().getTimeSystem().name());
814 
815         // Check data block
816         Assert.assertFalse(segment.getData().hasManeuvers());
817         FrameFacade sb = segment.getData().getQuaternionBlock().getEndpoints().getSpacecraftBodyFrame();
818         Assert.assertEquals(SpacecraftBodyFrame.BaseEquipment.SC_BODY, sb.asSpacecraftBodyFrame().getBaseEquipment());
819         Assert.assertEquals("1", sb.asSpacecraftBodyFrame().getLabel());
820         FrameFacade ext = segment.getData().getQuaternionBlock().getEndpoints().getExternalFrame();
821         Assert.assertNull(ext.asFrame());
822         Assert.assertNull(ext.asCelestialBodyFrame());
823         Assert.assertNull(ext.asOrbitRelativeFrame());
824         Assert.assertNull(ext.asSpacecraftBodyFrame());
825         Assert.assertEquals("UNKNOWN", ext.getName());
826         Assert.assertFalse(segment.getData().getQuaternionBlock().getEndpoints().isA2b());
827         Assert.assertEquals(new AbsoluteDate(2021, 1, 1, 0, 0, 0.0, TimeScalesFactory.getTDB()),
828                             segment.getData().getQuaternionBlock().getEpoch());
829 
830         try {
831             file.getAttitude(FramesFactory.getEME2000(),
832                              (date, frame) -> new TimeStampedPVCoordinates(date,
833                                                                            new PVCoordinates(new Vector3D( 1.234e7, -0.567e7, 9.876e6),
834                                                                                              new Vector3D(-0.772e4,  5.002e4, 4.892e2))));
835             Assert.fail("an exception should have been thrown");
836         } catch (OrekitException oe) {
837             Assert.assertEquals(OrekitMessages.CCSDS_INVALID_FRAME, oe.getSpecifier());
838             Assert.assertEquals(ext.getName(), oe.getParts()[0]);
839         }
840 
841     }
842 
843     @Test
844     public void testNotImplementedTimeSystems() {
845         try {
846             final String name = "/ccsds/adm/apm/APM-inconsistent-time-systems.txt";
847             final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
848             new ParserBuilder().
849             withMissionReferenceDate(AbsoluteDate.J2000_EPOCH).
850             buildApmParser().
851             parseMessage(source);
852             Assert.fail("an exception should have been thrown");
853         } catch (OrekitException oe) {
854             Assert.assertEquals(OrekitMessages.CCSDS_TIME_SYSTEM_NOT_IMPLEMENTED, oe.getSpecifier());
855             Assert.assertEquals("BCE", oe.getParts()[0]);
856         }
857     }
858 
859     @Test
860     public void testWrongADMType() {
861         final String name = "/ccsds/adm/aem/AEMExample01.txt";
862         try {
863             final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
864             new ParserBuilder().
865             withMissionReferenceDate(AbsoluteDate.J2000_EPOCH).
866             buildApmParser().
867             parseMessage(source);
868             Assert.fail("an exception should have been thrown");
869         } catch (OrekitException oe) {
870             Assert.assertEquals(OrekitMessages.UNSUPPORTED_FILE_FORMAT, oe.getSpecifier());
871             Assert.assertEquals(name, oe.getParts()[0]);
872         }
873     }
874 
875     @Test
876     public void testNumberFormatErrorType() {
877         final String name = "/ccsds/adm/apm/APM-number-format-error.txt";
878         try {
879             final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
880             new ParserBuilder().
881             withMissionReferenceDate(AbsoluteDate.J2000_EPOCH).
882             buildApmParser().
883             parseMessage(source);
884             Assert.fail("an exception should have been thrown");
885         } catch (OrekitException oe) {
886             Assert.assertEquals(OrekitMessages.UNABLE_TO_PARSE_ELEMENT_IN_FILE, oe.getSpecifier());
887             Assert.assertEquals("Q1", oe.getParts()[0]);
888             Assert.assertEquals(22, oe.getParts()[1]);
889             Assert.assertEquals(name, oe.getParts()[2]);
890         }
891     }
892 
893 
894     @Test
895     public void testNonExistentFile() throws URISyntaxException {
896         final String realName = getClass().getResource("/ccsds/adm/apm/APMExample1.txt").toURI().getPath();
897         final String wrongName = realName + "xxxxx";
898         try {
899             final DataSource source = new DataSource(wrongName, () -> getClass().getResourceAsStream(wrongName));
900             new ParserBuilder().
901             withMissionReferenceDate(AbsoluteDate.J2000_EPOCH).
902             buildApmParser().
903             parseMessage(source);
904             Assert.fail("an exception should have been thrown");
905         } catch (OrekitException oe) {
906             Assert.assertEquals(OrekitMessages.UNABLE_TO_FIND_FILE, oe.getSpecifier());
907             Assert.assertEquals(wrongName, oe.getParts()[0]);
908         }
909     }
910 
911     @Test
912     public void testMissingTwoSpacecraftFrames() throws URISyntaxException {
913         final String name = "/ccsds/adm/apm/APM-two-spacecraft-frames.txt";
914         try {
915             final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
916             new ParserBuilder().
917             withMissionReferenceDate(AbsoluteDate.J2000_EPOCH).
918             buildApmParser().
919             parseMessage(source);
920             Assert.fail("an exception should have been thrown");
921         } catch (OrekitException oe) {
922             Assert.assertEquals(OrekitMessages.CCSDS_INVALID_FRAME, oe.getSpecifier());
923             Assert.assertEquals("INSTRUMENT_2", oe.getParts()[0]);
924         }
925     }
926 
927     @Test
928     public void testNoSpacecraftFrames() throws URISyntaxException {
929         final String name = "/ccsds/adm/apm/APM-no-spacecraft-frames.txt";
930         try {
931             final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
932             new ParserBuilder().
933             withMissionReferenceDate(AbsoluteDate.J2000_EPOCH).
934             buildApmParser().
935             parseMessage(source);
936             Assert.fail("an exception should have been thrown");
937         } catch (OrekitException oe) {
938             Assert.assertEquals(OrekitMessages.CCSDS_INVALID_FRAME, oe.getSpecifier());
939             Assert.assertEquals("EME2000", oe.getParts()[0]);
940         }
941     }
942 
943     @Test
944     public void testMissingFrame() throws URISyntaxException {
945         final String name = "/ccsds/adm/apm/APM-missing-frame.txt";
946         try {
947             final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
948             new ParserBuilder().
949             withMissionReferenceDate(AbsoluteDate.J2000_EPOCH).
950             buildApmParser().
951             parseMessage(source);
952             Assert.fail("an exception should have been thrown");
953         } catch (OrekitException oe) {
954             Assert.assertEquals(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, oe.getSpecifier());
955             Assert.assertEquals("Q_FRAME_A", oe.getParts()[0]);
956         }
957     }
958 
959     @Test
960     public void testMissingQuaternionComponent() throws URISyntaxException {
961         final String name = "/ccsds/adm/apm/APM-missing-quaternion-component.txt";
962         try {
963             final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
964             new ParserBuilder().
965             withMissionReferenceDate(AbsoluteDate.J2000_EPOCH).
966             buildApmParser().
967             parseMessage(source);
968             Assert.fail("an exception should have been thrown");
969         } catch (OrekitException oe) {
970             Assert.assertEquals(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, oe.getSpecifier());
971             Assert.assertEquals("Q{C|1|2|3}", oe.getParts()[0]);
972         }
973     }
974 
975     @Test
976     public void testWrongKeyword() throws URISyntaxException {
977         final String name = "/ccsds/adm/apm/APM-wrong-keyword.txt";
978         try {
979             final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
980             new ParserBuilder().
981             withMissionReferenceDate(AbsoluteDate.J2000_EPOCH).
982             buildApmParser().
983             parseMessage(source);
984             Assert.fail("an exception should have been thrown");
985         } catch (OrekitException oe) {
986             Assert.assertEquals(OrekitMessages.CCSDS_UNEXPECTED_KEYWORD, oe.getSpecifier());
987             Assert.assertEquals(12, ((Integer) oe.getParts()[0]).intValue());
988             Assert.assertTrue(((String) oe.getParts()[2]).startsWith("WRONG_KEYWORD"));
989         }
990     }
991 
992     @Test
993     public void testWrongEulerSequence() throws URISyntaxException {
994         final String name = "/ccsds/adm/apm/APM-wrong-Euler-sequence.txt";
995         try {
996             final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
997             new ParserBuilder().
998             withMissionReferenceDate(AbsoluteDate.J2000_EPOCH).
999             buildApmParser().
1000             parseMessage(source);
1001             Assert.fail("an exception should have been thrown");
1002         } catch (OrekitException oe) {
1003             Assert.assertEquals(OrekitMessages.CCSDS_INVALID_ROTATION_SEQUENCE, oe.getSpecifier());
1004             Assert.assertEquals("331", oe.getParts()[0]);
1005             Assert.assertEquals(33, ((Integer) oe.getParts()[1]).intValue());
1006             Assert.assertEquals(name, oe.getParts()[2]);
1007         }
1008     }
1009 
1010     @Test
1011     public void testMissingEulerSequence() throws URISyntaxException {
1012         final String name = "/ccsds/adm/apm/APM-missing-Euler-sequence.txt";
1013         try {
1014             final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
1015             new ParserBuilder().
1016             withMissionReferenceDate(AbsoluteDate.J2000_EPOCH).
1017             buildApmParser().
1018             parseMessage(source);
1019             Assert.fail("an exception should have been thrown");
1020         } catch (OrekitException oe) {
1021             Assert.assertEquals(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, oe.getSpecifier());
1022             Assert.assertEquals("EULER_ROT_SEQ", oe.getParts()[0]);
1023         }
1024     }
1025 
1026     @Test
1027     public void testRepeatedEulerAngle() throws URISyntaxException {
1028         final String name = "/ccsds/adm/apm/APM-repeated-Euler-angle.txt";
1029         try {
1030             final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
1031             new ParserBuilder().
1032             withMissionReferenceDate(AbsoluteDate.J2000_EPOCH).
1033             buildApmParser().
1034             parseMessage(source);
1035             Assert.fail("an exception should have been thrown");
1036         } catch (OrekitException oe) {
1037             Assert.assertEquals(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, oe.getSpecifier());
1038             Assert.assertEquals("{X|Y|Z}_ANGLE", oe.getParts()[0]);
1039         }
1040     }
1041 
1042     @Test
1043     public void testIncompatibleframes() throws URISyntaxException {
1044         final String name = "/ccsds/adm/apm/APM-incompatible-frames.txt";
1045         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
1046         final Apm apm = new ParserBuilder().buildApmParser().parseMessage(source);
1047         Assert.assertNotNull(apm);
1048         try {
1049             apm.getAttitude(FramesFactory.getGCRF(), 
1050                             (date, frame) -> new TimeStampedPVCoordinates(date,
1051                                                                           new PVCoordinates(new Vector3D( 1.234e7, -0.567e7, 9.876e6),
1052                                                                                             new Vector3D(-0.772e4,  5.002e4, 4.892e2))));
1053             Assert.fail("an exception should have been thrown");
1054         } catch (OrekitException oe) {
1055             Assert.assertEquals(OrekitMessages.INCOMPATIBLE_FRAMES, oe.getSpecifier());
1056             Assert.assertEquals("SC_BODY_1 → ITRF-97", oe.getParts()[0]);
1057             Assert.assertEquals("SC_BODY_1 ← GCRF",    oe.getParts()[1]);
1058         }
1059     }
1060 
1061 }