1   /* Copyright 2022-2025 Luc Maisonobe
2    * Licensed to CS Systèmes d'Information (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.acm;
18  
19  import java.io.ByteArrayInputStream;
20  import java.io.CharArrayWriter;
21  import java.io.IOException;
22  import java.nio.charset.StandardCharsets;
23  import java.util.List;
24  import java.util.function.Function;
25  
26  import org.hamcrest.MatcherAssert;
27  import org.hamcrest.Matchers;
28  import org.hipparchus.geometry.euclidean.threed.Rotation;
29  import org.hipparchus.geometry.euclidean.threed.RotationConvention;
30  import org.hipparchus.geometry.euclidean.threed.RotationOrder;
31  import org.hipparchus.linear.DiagonalMatrix;
32  import org.hipparchus.util.FastMath;
33  import org.junit.jupiter.api.Assertions;
34  import org.junit.jupiter.api.BeforeEach;
35  import org.junit.jupiter.api.Test;
36  import org.orekit.Utils;
37  import org.orekit.data.DataSource;
38  import org.orekit.errors.OrekitException;
39  import org.orekit.errors.OrekitMessages;
40  import org.orekit.files.ccsds.definitions.AdMethodType;
41  import org.orekit.files.ccsds.definitions.BodyFacade;
42  import org.orekit.files.ccsds.definitions.CcsdsFrameMapper;
43  import org.orekit.files.ccsds.definitions.CenterName;
44  import org.orekit.files.ccsds.definitions.FrameFacade;
45  import org.orekit.files.ccsds.definitions.OrekitCcsdsFrameMapper;
46  import org.orekit.files.ccsds.definitions.SpacecraftBodyFrame.BaseEquipment;
47  import org.orekit.files.ccsds.ndm.ParserBuilder;
48  import org.orekit.files.ccsds.ndm.WriterBuilder;
49  import org.orekit.files.ccsds.utils.generation.Generator;
50  import org.orekit.files.ccsds.utils.generation.XmlGenerator;
51  import org.orekit.files.ccsds.utils.lexical.KvnLexicalAnalyzer;
52  import org.orekit.files.ccsds.utils.lexical.XmlLexicalAnalyzer;
53  import org.orekit.frames.Frame;
54  import org.orekit.frames.FramesFactory;
55  import org.orekit.frames.Transform;
56  import org.orekit.time.AbsoluteDate;
57  import org.orekit.time.TimeOffset;
58  import org.orekit.time.TimeScale;
59  import org.orekit.time.TimeScalesFactory;
60  import org.orekit.utils.Constants;
61  
62  public class AcmParserTest {
63  
64      @BeforeEach
65      public void setUp() {
66          Utils.setDataRoot("regular-data");
67      }
68  
69      @Test
70      public void testNonExistentKvnFile() {
71          final String realName = "/ccsds/adm/acm/ACMExample01.txt";
72          final String wrongName = realName + "xxxxx";
73          final DataSource source = new DataSource(wrongName, () -> getClass().getResourceAsStream(wrongName));
74          try {
75              new KvnLexicalAnalyzer(source).accept(new ParserBuilder().buildAcmParser());
76              Assertions.fail("an exception should have been thrown");
77          } catch (OrekitException oe) {
78              Assertions.assertEquals(OrekitMessages.UNABLE_TO_FIND_FILE, oe.getSpecifier());
79              Assertions.assertEquals(wrongName, oe.getParts()[0]);
80          }
81      }
82  
83      @Test
84      public void testNonExistentXmlFile() {
85          final String realName = "/ccsds/adm/acm/ACMExample01.txt";
86          final String wrongName = realName + "xxxxx";
87          final DataSource source = new DataSource(wrongName, () -> getClass().getResourceAsStream(wrongName));
88          try {
89              new XmlLexicalAnalyzer(source).accept(new ParserBuilder().buildAcmParser());
90              Assertions.fail("an exception should have been thrown");
91          } catch (OrekitException oe) {
92              Assertions.assertEquals(OrekitMessages.UNABLE_TO_FIND_FILE, oe.getSpecifier());
93              Assertions.assertEquals(wrongName, oe.getParts()[0]);
94          }
95      }
96  
97      @Test
98      public void testIncompatibleKeys() {
99          final String name = "/ccsds/adm/acm/incompatible-keys.txt";
100         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
101         try {
102             new ParserBuilder().buildAcmParser().parseMessage(source);
103             Assertions.fail("an exception should have been thrown");
104         } catch (OrekitException oe) {
105             Assertions.assertEquals(OrekitMessages.CCSDS_INCOMPATIBLE_KEYS_BOTH_USED, oe.getSpecifier());
106             Assertions.assertEquals(AttitudeManeuverKey.MAN_END_TIME, oe.getParts()[0]);
107             Assertions.assertEquals(AttitudeManeuverKey.MAN_DURATION, oe.getParts()[1]);
108         }
109     }
110 
111     @Test
112     public void testSensorIndexAlreadyUsed() {
113         final String name = "/ccsds/adm/acm/sensor-index-already-used.txt";
114         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
115         try {
116             new ParserBuilder().buildAcmParser().parse(source);
117             Assertions.fail("an exception should have been thrown");
118         } catch (OrekitException oe) {
119             Assertions.assertEquals(OrekitMessages.CCSDS_SENSOR_INDEX_ALREADY_USED, oe.getSpecifier());
120             Assertions.assertEquals(2, ((Integer) oe.getParts()[0]).intValue());
121         }
122     }
123 
124     @Test
125     public void testMissingSensorIndex() {
126         final String name = "/ccsds/adm/acm/missing-sensor-index.txt";
127         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
128         try {
129             new ParserBuilder().buildAcmParser().parseMessage(source);
130             Assertions.fail("an exception should have been thrown");
131         } catch (OrekitException oe) {
132             Assertions.assertEquals(OrekitMessages.CCSDS_MISSING_SENSOR_INDEX, oe.getSpecifier());
133             Assertions.assertEquals(3, ((Integer) oe.getParts()[0]).intValue());
134         }
135     }
136 
137     @Test
138     public void testWrongStdDevNumber() {
139         final String name = "/ccsds/adm/acm/wrong-stddev-number.txt";
140         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
141         try {
142             new ParserBuilder().buildAcmParser().parseMessage(source);
143             Assertions.fail("an exception should have been thrown");
144         } catch (OrekitException oe) {
145             Assertions.assertEquals(OrekitMessages.INCONSISTENT_NUMBER_OF_ELEMENTS, oe.getSpecifier());
146             Assertions.assertEquals(1, ((Integer) oe.getParts()[0]).intValue());
147             Assertions.assertEquals(2, ((Integer) oe.getParts()[1]).intValue());
148         }
149     }
150 
151     @Test
152     public void testSpuriousMetaDataSection() {
153         final String name = "/ccsds/adm/acm/spurious-metadata.txt";
154         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
155         try {
156             new ParserBuilder().buildAcmParser().parseMessage(source);
157             Assertions.fail("an exception should have been thrown");
158         } catch (OrekitException oe) {
159             Assertions.assertEquals(OrekitMessages.CCSDS_UNEXPECTED_KEYWORD, oe.getSpecifier());
160             Assertions.assertEquals(13, ((Integer) oe.getParts()[0]).intValue());
161             Assertions.assertEquals("META", oe.getParts()[2]);
162         }
163     }
164 
165     @Test
166     public void testMissingTargetMomentum() {
167         final String name = "/ccsds/adm/acm/missing-target-momentum.txt";
168         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
169         try {
170             new ParserBuilder().buildAcmParser().parseMessage(source);
171             Assertions.fail("an exception should have been thrown");
172         } catch (OrekitException oe) {
173             Assertions.assertEquals(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, oe.getSpecifier());
174             Assertions.assertEquals(AttitudeManeuverKey.TARGET_MOMENTUM.name(), oe.getParts()[0]);
175         }
176     }
177 
178     @Test
179     public void testMissingCenterOfPressure() {
180         final String name = "/ccsds/adm/acm/missing-center-of-pressure.txt";
181         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
182         try {
183             new ParserBuilder().buildAcmParser().parseMessage(source);
184             Assertions.fail("an exception should have been thrown");
185         } catch (OrekitException oe) {
186             Assertions.assertEquals(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, oe.getSpecifier());
187             Assertions.assertEquals(AttitudePhysicalPropertiesKey.CP.name(), oe.getParts()[0]);
188         }
189     }
190 
191     @Test
192     public void testParseACM01() {
193         final String   name  = "/ccsds/adm/acm/ACMExample01.txt";
194         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
195         final Acm    acm    = new ParserBuilder().buildAcmParser().parseMessage(source);
196 
197         // Check Header Block;
198         Assertions.assertEquals(2.0, acm.getHeader().getFormatVersion(), 1.0e-10);
199         Assertions.assertEquals("unrestricted", acm.getHeader().getClassification());
200         Assertions.assertEquals(new AbsoluteDate(1998, 11, 6, 9, 23, 57, TimeScalesFactory.getUTC()),
201                                 acm.getHeader().getCreationDate());
202         Assertions.assertEquals("JAXA", acm.getHeader().getOriginator());
203         Assertions.assertEquals("A7015Z4", acm.getHeader().getMessageId());
204 
205         // metadata
206         Assertions.assertEquals("EUROBIRD-4A", acm.getMetadata().getObjectName());
207         Assertions.assertEquals("2000-052A",   acm.getMetadata().getInternationalDesignator());
208         Assertions.assertEquals(2000,          acm.getMetadata().getLaunchYear());
209         Assertions.assertEquals(52,            acm.getMetadata().getLaunchNumber());
210         Assertions.assertEquals("A",           acm.getMetadata().getLaunchPiece());
211         Assertions.assertEquals("UTC",         acm.getMetadata().getTimeSystem().name());
212         Assertions.assertEquals(new AbsoluteDate(1998, 12, 18, 14, 28, new TimeOffset(15, TimeOffset.SECOND, 117200, TimeOffset.MICROSECOND), TimeScalesFactory.getUTC()),
213                                 acm.getMetadata().getEpochT0());        
214 
215         // attitude data
216         Assertions.assertEquals(1, acm.getData().getAttitudeBlocks().size());
217         AttitudeStateHistory history = acm.getData().getAttitudeBlocks().get(0);
218         Assertions.assertTrue(history.getMetadata().getComments().isEmpty());
219         Assertions.assertNull(history.getMetadata().getAttID());
220         Assertions.assertNull(history.getMetadata().getAttPrevID());
221         Assertions.assertNull(history.getMetadata().getAttBasis());
222         Assertions.assertNull(history.getMetadata().getAttBasisID());
223         Assertions.assertEquals("J2000", history.getMetadata().getEndpoints().getFrameA().getName());
224         Assertions.assertEquals(BaseEquipment.SC_BODY, history.getMetadata().getEndpoints().getFrameB().asSpacecraftBodyFrame().getBaseEquipment());
225         Assertions.assertEquals("1", history.getMetadata().getEndpoints().getFrameB().asSpacecraftBodyFrame().getLabel());
226         Assertions.assertEquals(4, history.getMetadata().getNbStates());
227         Assertions.assertEquals(AttitudeElementsType.QUATERNION, history.getMetadata().getAttitudeType());
228         Assertions.assertNull(history.getMetadata().getRateType());
229         List<AttitudeState> states = history.getAttitudeStates();
230         Assertions.assertEquals(3, states.size());
231 
232         Assertions.assertEquals(0.0, states.get(0).getDate().durationFrom(acm.getMetadata().getEpochT0()), 1.0e-15);
233         Assertions.assertEquals(4,   states.get(0).getElements().length);
234         Assertions.assertEquals( 0.73566,  states.get(0).getElements()[0], 1.0e-15);
235         Assertions.assertEquals(-0.50547,  states.get(0).getElements()[1], 1.0e-15);
236         Assertions.assertEquals( 0.41309,  states.get(0).getElements()[2], 1.0e-15);
237         Assertions.assertEquals( 0.180707, states.get(0).getElements()[3], 1.0e-15);
238 
239         Assertions.assertEquals(0.25, states.get(1).getDate().durationFrom(acm.getMetadata().getEpochT0()), 1.0e-15);
240         Assertions.assertEquals(4,    states.get(1).getElements().length);
241         Assertions.assertEquals( 0.73529,  states.get(1).getElements()[0], 1.0e-15);
242         Assertions.assertEquals(-0.50531,  states.get(1).getElements()[1], 1.0e-15);
243         Assertions.assertEquals( 0.41375,  states.get(1).getElements()[2], 1.0e-15);
244         Assertions.assertEquals( 0.181158, states.get(1).getElements()[3], 1.0e-15);
245 
246         Assertions.assertEquals(0.50, states.get(2).getDate().durationFrom(acm.getMetadata().getEpochT0()), 1.0e-15);
247         Assertions.assertEquals(4,    states.get(2).getElements().length);
248         Assertions.assertEquals( 0.73492,  states.get(2).getElements()[0], 1.0e-15);
249         Assertions.assertEquals(-0.50515,  states.get(2).getElements()[1], 1.0e-15);
250         Assertions.assertEquals( 0.41441,  states.get(2).getElements()[2], 1.0e-15);
251         Assertions.assertEquals( 0.181610, states.get(2).getElements()[3], 1.0e-15);
252 
253         Assertions.assertNull(acm.getSegments().get(0).getData().getPhysicBlock());
254         Assertions.assertNull(acm.getSegments().get(0).getData().getCovarianceBlocks());
255         Assertions.assertNull(acm.getSegments().get(0).getData().getManeuverBlocks());
256         Assertions.assertNull(acm.getSegments().get(0).getData().getAttitudeDeterminationBlock());
257         Assertions.assertNull(acm.getSegments().get(0).getData().getUserDefinedBlock());
258 
259     }
260 
261     @Test
262     public void testParseACM02() {
263         final String   name  = "/ccsds/adm/acm/ACMExample02.txt";
264         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
265         final Acm    acm    = new ParserBuilder().buildAcmParser().parseMessage(source);
266 
267         // Check Header Block;
268         Assertions.assertEquals(2.0, acm.getHeader().getFormatVersion(), 1.0e-10);
269         Assertions.assertNull(acm.getHeader().getClassification());
270         Assertions.assertEquals(new AbsoluteDate(2017, 12, 1, TimeScalesFactory.getUTC()),
271                                 acm.getHeader().getCreationDate());
272         Assertions.assertEquals("NASA", acm.getHeader().getOriginator());
273         Assertions.assertEquals("A7015Z5", acm.getHeader().getMessageId());
274 
275         // metadata
276         Assertions.assertEquals("SDO",         acm.getMetadata().getObjectName());
277         Assertions.assertEquals("2010-005A",   acm.getMetadata().getInternationalDesignator());
278         Assertions.assertEquals(2010,          acm.getMetadata().getLaunchYear());
279         Assertions.assertEquals(5,             acm.getMetadata().getLaunchNumber());
280         Assertions.assertEquals("A",           acm.getMetadata().getLaunchPiece());
281         Assertions.assertEquals("UTC",         acm.getMetadata().getTimeSystem().name());
282         Assertions.assertEquals(new AbsoluteDate(2017, 12, 26, 19, 40, 0.0, TimeScalesFactory.getUTC()),
283                                 acm.getMetadata().getEpochT0());        
284 
285         // attitude data
286         Assertions.assertEquals(1, acm.getData().getAttitudeBlocks().size());
287         AttitudeStateHistory history = acm.getData().getAttitudeBlocks().get(0);
288         Assertions.assertEquals("OBC Attitude and Bias during momentum management maneuver",
289                                 history.getMetadata().getComments().get(0));
290         Assertions.assertNull(history.getMetadata().getAttID());
291         Assertions.assertNull(history.getMetadata().getAttPrevID());
292         Assertions.assertNull(history.getMetadata().getAttBasis());
293         Assertions.assertNull(history.getMetadata().getAttBasisID());
294         Assertions.assertEquals("J2000", history.getMetadata().getEndpoints().getFrameA().getName());
295         Assertions.assertEquals(BaseEquipment.SC_BODY, history.getMetadata().getEndpoints().getFrameB().asSpacecraftBodyFrame().getBaseEquipment());
296         Assertions.assertEquals("1", history.getMetadata().getEndpoints().getFrameB().asSpacecraftBodyFrame().getLabel());
297         Assertions.assertEquals(7, history.getMetadata().getNbStates());
298         Assertions.assertEquals(AttitudeElementsType.QUATERNION, history.getMetadata().getAttitudeType());
299         Assertions.assertEquals(RateElementsType.GYRO_BIAS, history.getMetadata().getRateType());
300         List<AttitudeState> states = history.getAttitudeStates();
301         Assertions.assertEquals(4, states.size());
302 
303         Assertions.assertEquals(0.0, states.get(0).getDate().durationFrom(acm.getMetadata().getEpochT0()), 1.0e-15);
304         Assertions.assertEquals(7,   states.get(0).getElements().length);
305         Assertions.assertEquals( 0.1153,    states.get(0).getElements()[0], 1.0e-15);
306         Assertions.assertEquals(-0.1424,    states.get(0).getElements()[1], 1.0e-15);
307         Assertions.assertEquals( 0.8704,    states.get(0).getElements()[2], 1.0e-15);
308         Assertions.assertEquals( 0.4571,    states.get(0).getElements()[3], 1.0e-15);
309         Assertions.assertEquals( 2.271e-06, FastMath.toDegrees(states.get(0).getElements()[4]), 1.0e-15);
310         Assertions.assertEquals(-4.405e-06, FastMath.toDegrees(states.get(0).getElements()[5]), 1.0e-15);
311         Assertions.assertEquals(-3.785e-06, FastMath.toDegrees(states.get(0).getElements()[6]), 1.0e-15);
312 
313         Assertions.assertEquals(1, acm.getData().getManeuverBlocks().size());
314         AttitudeManeuver man = acm.getData().getManeuverBlocks().get(0);
315         Assertions.assertEquals("Momentum management maneuver", man.getComments().get(0));
316         Assertions.assertEquals("MOM_DESAT",                    man.getManPurpose());
317         Assertions.assertEquals(100.0,                          man.getBeginTime());
318         Assertions.assertTrue(Double.isNaN(man.getEndTime()));
319         Assertions.assertEquals(450.0,                          man.getDuration());
320         Assertions.assertEquals("ATT-THRUSTER",                 man.getActuatorUsed());
321         Assertions.assertEquals(  1.3,                          man.getTargetMomentum().getX(), 1.0e-10);
322         Assertions.assertEquals(-16.4,                          man.getTargetMomentum().getY(), 1.0e-10);
323         Assertions.assertEquals(-11.35,                         man.getTargetMomentum().getZ(), 1.0e-10);
324         Assertions.assertNull(man.getTargetAttitude());
325         Assertions.assertTrue(Double.isNaN(man.getTargetSpinRate()));
326 
327         AttitudeDetermination ad = acm.getData().getAttitudeDeterminationBlock();
328         Assertions.assertEquals("SDO Onboard Filter",    ad.getComments().get(0));
329         Assertions.assertEquals(AdMethodType.EKF,        ad.getMethod());
330         Assertions.assertEquals("OBC",                   ad.getSource());
331         Assertions.assertEquals(AttitudeElementsType.QUATERNION, ad.getAttitudeStates());
332         Assertions.assertEquals("J2000",                 ad.getEndpoints().getFrameA().getName());
333         Assertions.assertEquals(BaseEquipment.SC_BODY,   ad.getEndpoints().getFrameB().asSpacecraftBodyFrame().getBaseEquipment());
334         Assertions.assertEquals("1",                     ad.getEndpoints().getFrameB().asSpacecraftBodyFrame().getLabel());
335         Assertions.assertEquals(4,                       ad.getSensorsUsed().size());
336         Assertions.assertEquals("AST1",                  ad.getSensorsUsed().get(0).getSensorUsed());
337         Assertions.assertEquals("AST2",                  ad.getSensorsUsed().get(1).getSensorUsed());
338         Assertions.assertEquals("DSS",                   ad.getSensorsUsed().get(2).getSensorUsed());
339         Assertions.assertEquals("IMU",                   ad.getSensorsUsed().get(3).getSensorUsed());
340 
341     }
342 
343     @Test
344     public void testParseACM03() {
345         final String     name   = "/ccsds/adm/acm/ACMExample03.txt";
346         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
347         final Acm        acm    = new ParserBuilder().buildAcmParser().parseMessage(source);
348 
349         // Check Header Block;
350         Assertions.assertEquals(2.0, acm.getHeader().getFormatVersion(), 1.0e-10);
351        Assertions.assertEquals(new AbsoluteDate(1998, 11, 6, 9, 23, 57, TimeScalesFactory.getUTC()),
352                                 acm.getHeader().getCreationDate());
353         Assertions.assertEquals("JAXA", acm.getHeader().getOriginator());
354         Assertions.assertEquals("A7015Z6", acm.getHeader().getMessageId());
355 
356         // metadata
357         Assertions.assertEquals("TEST_SAT",    acm.getMetadata().getObjectName());
358         Assertions.assertNull(acm.getMetadata().getInternationalDesignator());
359         Assertions.assertEquals("TAI",         acm.getMetadata().getTimeSystem().name());
360         Assertions.assertEquals(new AbsoluteDate(1998, 12, 18, 14, 28, new TimeOffset(15, TimeOffset.SECOND, 117200, TimeOffset.MICROSECOND), TimeScalesFactory.getTAI()),
361                                 acm.getMetadata().getEpochT0());  
362         Assertions.assertEquals(36.0,          acm.getMetadata().getTaimutcT0(), 1.0e-15);
363 
364         Assertions.assertNull(acm.getData().getAttitudeBlocks());
365         AttitudePhysicalProperties phys = acm.getData().getPhysicBlock();
366         Assertions.assertEquals("Spacecraft Physical Parameters", phys.getComments().get(0));
367         Assertions.assertEquals(1916.0, phys.getWetMass(), 1.0e-15);
368         Assertions.assertEquals( 0.04,  phys.getCenterOfPressure().getX(),      1.0e-15);
369         Assertions.assertEquals(-0.78,  phys.getCenterOfPressure().getY(),      1.0e-15);
370         Assertions.assertEquals(-0.023, phys.getCenterOfPressure().getZ(),      1.0e-15);
371         Assertions.assertEquals( 752.0, phys.getInertiaMatrix().getEntry(0, 0), 1.0e-15);
372         Assertions.assertEquals(1305.0, phys.getInertiaMatrix().getEntry(1, 1), 1.0e-15);
373         Assertions.assertEquals(1490.0, phys.getInertiaMatrix().getEntry(2, 2), 1.0e-15);
374         Assertions.assertEquals(  81.1, phys.getInertiaMatrix().getEntry(0, 1), 1.0e-15);
375         Assertions.assertEquals(  81.1, phys.getInertiaMatrix().getEntry(1, 0), 1.0e-15);
376         Assertions.assertEquals( -25.7, phys.getInertiaMatrix().getEntry(0, 2), 1.0e-15);
377         Assertions.assertEquals( -25.7, phys.getInertiaMatrix().getEntry(2, 0), 1.0e-15);
378         Assertions.assertEquals(  74.1, phys.getInertiaMatrix().getEntry(1, 2), 1.0e-15);
379         Assertions.assertEquals(  74.1, phys.getInertiaMatrix().getEntry(2, 1), 1.0e-15);
380 
381     }
382 
383     @Test
384     public void testParseACM04() {
385         final String   name  = "/ccsds/adm/acm/ACMExample04.txt";
386         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
387         final Acm    acm    = new ParserBuilder().buildAcmParser().parseMessage(source);
388 
389         // Check Header Block;
390         Assertions.assertEquals(2.0, acm.getHeader().getFormatVersion(), 1.0e-10);
391         Assertions.assertEquals(new AbsoluteDate(2017, 12, 30, TimeScalesFactory.getUTC()),
392                                 acm.getHeader().getCreationDate());
393         Assertions.assertEquals("NASA", acm.getHeader().getOriginator());
394         Assertions.assertEquals("A7015Z7", acm.getHeader().getMessageId());
395 
396         // metadata
397         Assertions.assertEquals("LRO",         acm.getMetadata().getObjectName());
398         Assertions.assertEquals("2009-031A",   acm.getMetadata().getInternationalDesignator());
399         Assertions.assertEquals(2009,          acm.getMetadata().getLaunchYear());
400         Assertions.assertEquals(31,            acm.getMetadata().getLaunchNumber());
401         Assertions.assertEquals("A",           acm.getMetadata().getLaunchPiece());
402         Assertions.assertEquals("UTC",         acm.getMetadata().getTimeSystem().name());
403         Assertions.assertEquals(new AbsoluteDate(2017, 12, 30, 0, 0, 0.0, TimeScalesFactory.getUTC()),
404                                 acm.getMetadata().getEpochT0()); 
405         Assertions.assertEquals(2,             acm.getMetadata().getAcmDataElements().size());
406         Assertions.assertEquals(AcmElements.COV, acm.getMetadata().getAcmDataElements().get(0));
407         Assertions.assertEquals(AcmElements.AD,  acm.getMetadata().getAcmDataElements().get(1));
408 
409         // covariance data
410         Assertions.assertEquals(1, acm.getData().getCovarianceBlocks().size());
411         AttitudeCovarianceHistory history = acm.getData().getCovarianceBlocks().get(0);
412         Assertions.assertEquals("Diagonal Covariance for LRO Onboard Kalman Filter", history.getMetadata().getComments().get(0));
413         Assertions.assertEquals("DETERMINED_OBC", history.getMetadata().getCovBasis());
414         Assertions.assertEquals(BaseEquipment.SC_BODY, history.getMetadata().getCovReferenceFrame().asSpacecraftBodyFrame().getBaseEquipment());
415         Assertions.assertEquals("1", history.getMetadata().getCovReferenceFrame().asSpacecraftBodyFrame().getLabel());
416         Assertions.assertEquals(AttitudeCovarianceType.ANGLE_GYROBIAS, history.getMetadata().getCovType());
417         List<AttitudeCovariance> covariances = history.getCovariances();
418         Assertions.assertEquals(3, covariances.size());
419 
420         Assertions.assertEquals(0.0, covariances.get(0).getDate().durationFrom(acm.getMetadata().getEpochT0()), 1.0e-15);
421         DiagonalMatrix m = covariances.get(0).getMatrix();
422         Assertions.assertEquals(6,   m.getRowDimension());
423         Assertions.assertEquals( 6.74E-11, FastMath.toDegrees(FastMath.toDegrees(m.getEntry(0, 0))), 1.0e-22);
424         Assertions.assertEquals( 8.10E-11, FastMath.toDegrees(FastMath.toDegrees(m.getEntry(1, 1))), 1.0e-22);
425         Assertions.assertEquals( 9.22E-11, FastMath.toDegrees(FastMath.toDegrees(m.getEntry(2, 2))), 1.0e-22);
426         Assertions.assertEquals( 1.11E-15, FastMath.toDegrees(FastMath.toDegrees(m.getEntry(3, 3))), 1.0e-22);
427         Assertions.assertEquals( 1.11E-15, FastMath.toDegrees(FastMath.toDegrees(m.getEntry(4, 4))), 1.0e-22);
428         Assertions.assertEquals( 1.12E-15, FastMath.toDegrees(FastMath.toDegrees(m.getEntry(5, 5))), 1.0e-22);
429 
430         AttitudeDetermination ad = acm.getData().getAttitudeDeterminationBlock();
431         Assertions.assertEquals("LRO Onboard Filter, A Multiplicative Extended Kalman Filter", ad.getComments().get(0));
432         Assertions.assertEquals(AdMethodType.EKF,                      ad.getMethod());
433         Assertions.assertEquals("OBC",                                 ad.getSource());
434         Assertions.assertEquals(7,                                     ad.getNbStates());
435         Assertions.assertEquals(AttitudeElementsType.QUATERNION,       ad.getAttitudeStates());
436         Assertions.assertEquals(AttitudeCovarianceType.ANGLE_GYROBIAS, ad.getCovarianceType());
437         Assertions.assertEquals("EME2000",                             ad.getEndpoints().getFrameA().getName());
438         Assertions.assertEquals(BaseEquipment.SC_BODY,                 ad.getEndpoints().getFrameB().asSpacecraftBodyFrame().getBaseEquipment());
439         Assertions.assertEquals("1",                                   ad.getEndpoints().getFrameB().asSpacecraftBodyFrame().getLabel());
440         Assertions.assertEquals(RateElementsType.GYRO_BIAS,            ad.getRateStates());
441         Assertions.assertEquals(3,                                     ad.getSensorsUsed().size());
442         Assertions.assertEquals("AST1",                                ad.getSensorsUsed().get(0).getSensorUsed());
443         Assertions.assertEquals("AST2",                                ad.getSensorsUsed().get(1).getSensorUsed());
444         Assertions.assertEquals("IMU",                                 ad.getSensorsUsed().get(2).getSensorUsed());
445 
446     }
447 
448     @Test
449     public void testParseACM05() {
450         final String   name  = "/ccsds/adm/acm/ACMExample05.txt";
451         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
452         validateAcm05(new ParserBuilder().buildAcmParser().parseMessage(source));
453     }
454 
455     private void validateAcm05(Acm acm) {
456 
457         final AbsoluteDate t0 = new AbsoluteDate(2016, 3, 15, 0, 0, 0.0, TimeScalesFactory.getUTC());
458 
459         // Check Header Block;
460         Assertions.assertEquals(2.0, acm.getHeader().getFormatVersion(), 1.0e-10);
461         Assertions.assertEquals(2, acm.getHeader().getComments().size());
462         Assertions.assertEquals("This is an arbitrary test file with probably inconsistent data",
463                                 acm.getHeader().getComments().get(0));
464         Assertions.assertEquals("its purpose is only to exercise all possible entries in ACM files",
465                                 acm.getHeader().getComments().get(1));
466         Assertions.assertEquals("free to use under Orekit license",  acm.getHeader().getClassification());
467         Assertions.assertEquals(new AbsoluteDate(2023, 4, 8, 14, 31, 0.0, TimeScalesFactory.getUTC()),
468                                 acm.getHeader().getCreationDate());
469         Assertions.assertEquals("OREKIT", acm.getHeader().getOriginator());
470         Assertions.assertEquals("a4830b29-a805-4d31-ab6e-06b57c843323", acm.getHeader().getMessageId());
471 
472         // metadata
473         Assertions.assertEquals("comment at metadata start",    acm.getMetadata().getComments().get(0));
474         Assertions.assertEquals("Korrigan",                     acm.getMetadata().getObjectName());
475         Assertions.assertEquals("1703-999Z",                    acm.getMetadata().getInternationalDesignator());
476         Assertions.assertEquals(1703,                           acm.getMetadata().getLaunchYear());
477         Assertions.assertEquals(999,                            acm.getMetadata().getLaunchNumber());
478         Assertions.assertEquals("Z",                            acm.getMetadata().getLaunchPiece());
479         Assertions.assertEquals("FAERY",                        acm.getMetadata().getCatalogName());
480         Assertions.assertEquals("Korrik-17",                    acm.getMetadata().getObjectDesignator());
481         Assertions.assertEquals("Melusine",                     acm.getMetadata().getOriginatorPOC());
482         Assertions.assertEquals("Odonatoptera-lead",            acm.getMetadata().getOriginatorPosition());
483         Assertions.assertEquals("+9911223344",                  acm.getMetadata().getOriginatorPhone());
484         Assertions.assertEquals("melusine@avalon.surreal",      acm.getMetadata().getOriginatorEmail());
485         Assertions.assertEquals("-1 dolmen avenue, Stonehenge", acm.getMetadata().getOriginatorAddress());
486         Assertions.assertEquals("odm-7c32f8a9c126432f",         acm.getMetadata().getOdmMessageLink());
487         Assertions.assertEquals(CenterName.MOON.name(),         acm.getMetadata().getCenter().getName());
488         Assertions.assertEquals("UTC",                          acm.getMetadata().getTimeSystem().name());
489         Assertions.assertEquals(t0,                             acm.getMetadata().getEpochT0()); 
490         Assertions.assertEquals(17, acm.getMetadata().getAcmDataElements().size());
491         Assertions.assertEquals(AcmElements.ATT,  acm.getMetadata().getAcmDataElements().get( 0));
492         Assertions.assertEquals(AcmElements.ATT,  acm.getMetadata().getAcmDataElements().get( 1));
493         Assertions.assertEquals(AcmElements.ATT,  acm.getMetadata().getAcmDataElements().get( 2));
494         Assertions.assertEquals(AcmElements.ATT,  acm.getMetadata().getAcmDataElements().get( 3));
495         Assertions.assertEquals(AcmElements.ATT,  acm.getMetadata().getAcmDataElements().get( 4));
496         Assertions.assertEquals(AcmElements.PHYS, acm.getMetadata().getAcmDataElements().get( 5));
497         Assertions.assertEquals(AcmElements.COV,  acm.getMetadata().getAcmDataElements().get( 6));
498         Assertions.assertEquals(AcmElements.COV,  acm.getMetadata().getAcmDataElements().get( 7));
499         Assertions.assertEquals(AcmElements.COV,  acm.getMetadata().getAcmDataElements().get( 8));
500         Assertions.assertEquals(AcmElements.COV,  acm.getMetadata().getAcmDataElements().get( 9));
501         Assertions.assertEquals(AcmElements.COV,  acm.getMetadata().getAcmDataElements().get(10));
502         Assertions.assertEquals(AcmElements.COV,  acm.getMetadata().getAcmDataElements().get(11));
503         Assertions.assertEquals(AcmElements.MAN,  acm.getMetadata().getAcmDataElements().get(12));
504         Assertions.assertEquals(AcmElements.MAN,  acm.getMetadata().getAcmDataElements().get(13));
505         Assertions.assertEquals(AcmElements.MAN,  acm.getMetadata().getAcmDataElements().get(14));
506         Assertions.assertEquals(AcmElements.AD,   acm.getMetadata().getAcmDataElements().get(15));
507         Assertions.assertEquals(AcmElements.USER, acm.getMetadata().getAcmDataElements().get(16));
508         Assertions.assertEquals(18600.0,          acm.getMetadata().getStartTime().durationFrom(t0), 1.0e-12);
509         Assertions.assertEquals(19000.0,          acm.getMetadata().getStopTime().durationFrom(t0), 1.0e-12);
510         Assertions.assertEquals(36,               acm.getMetadata().getTaimutcT0());
511         Assertions.assertEquals(0.0,
512                                 acm.getMetadata().getNextLeapEpoch().durationFrom(new AbsoluteDate("2017-01-01", TimeScalesFactory.getUTC())),
513                                 1.0e-12);
514         Assertions.assertEquals(37,               acm.getMetadata().getNextLeapTaimutc());
515 
516         Assertions.assertEquals(5, acm.getData().getAttitudeBlocks().size());
517 
518         // first attitude block
519         AttitudeStateHistory att1 = acm.getData().getAttitudeBlocks().get(0);
520         Assertions.assertEquals("first attitude block",          att1.getMetadata().getComments().get(0));
521         Assertions.assertEquals("ATT_1",                         att1.getMetadata().getAttID());
522         Assertions.assertEquals("ATT_0",                         att1.getMetadata().getAttPrevID());
523         Assertions.assertEquals("SIMULATED",                     att1.getMetadata().getAttBasis());
524         Assertions.assertEquals("rnd-25",                        att1.getMetadata().getAttBasisID());
525         Assertions.assertEquals("EME2000",                       att1.getMetadata().getEndpoints().getFrameA().getName());
526         Assertions.assertEquals(BaseEquipment.GYRO,              att1.getMetadata().getEndpoints().getFrameB().asSpacecraftBodyFrame().getBaseEquipment());
527         Assertions.assertEquals("3",                             att1.getMetadata().getEndpoints().getFrameB().asSpacecraftBodyFrame().getLabel());
528         Assertions.assertEquals(4,                               att1.getMetadata().getNbStates());
529         Assertions.assertEquals(AttitudeElementsType.QUATERNION, att1.getMetadata().getAttitudeType());
530         Assertions.assertEquals(RateElementsType.NONE,           att1.getMetadata().getRateType());
531         Assertions.assertEquals(2,                               att1.getAttitudeStates().size());
532         Assertions.assertEquals(0.0,       att1.getAttitudeStates().get(0).getDate().durationFrom(acm.getMetadata().getEpochT0()), 1.0e-15);
533         Assertions.assertEquals(4,         att1.getAttitudeStates().get(0).getElements().length);
534         Assertions.assertEquals( 0.73566,  att1.getAttitudeStates().get(0).getElements()[0], 1.0e-15);
535         Assertions.assertEquals(-0.50547,  att1.getAttitudeStates().get(0).getElements()[1], 1.0e-15);
536         Assertions.assertEquals( 0.41309,  att1.getAttitudeStates().get(0).getElements()[2], 1.0e-15);
537         Assertions.assertEquals( 0.180707, att1.getAttitudeStates().get(0).getElements()[3], 1.0e-15);
538         Assertions.assertEquals(0.25,      att1.getAttitudeStates().get(1).getDate().durationFrom(acm.getMetadata().getEpochT0()), 1.0e-15);
539         Assertions.assertEquals(4,         att1.getAttitudeStates().get(1).getElements().length);
540         Assertions.assertEquals( 0.73529,  att1.getAttitudeStates().get(1).getElements()[0], 1.0e-15);
541         Assertions.assertEquals(-0.50531,  att1.getAttitudeStates().get(1).getElements()[1], 1.0e-15);
542         Assertions.assertEquals( 0.41375,  att1.getAttitudeStates().get(1).getElements()[2], 1.0e-15);
543         Assertions.assertEquals( 0.181158, att1.getAttitudeStates().get(1).getElements()[3], 1.0e-15);
544         Assertions.assertEquals(0.0, att1.getAttitudeStates().get(0).toAngular(RotationOrder.XYZ).getRotationRate().getNorm(), 1.0e-10);
545 
546         // second attitude block
547         AttitudeStateHistory att2 = acm.getData().getAttitudeBlocks().get(1);
548         Assertions.assertEquals("second attitude block",           att2.getMetadata().getComments().get(0));
549         Assertions.assertEquals("ATT_2",                           att2.getMetadata().getAttID());
550         Assertions.assertEquals("ATT_1",                           att2.getMetadata().getAttPrevID());
551         Assertions.assertEquals("SIMULATED",                       att2.getMetadata().getAttBasis());
552         Assertions.assertEquals("rnd-25",                          att2.getMetadata().getAttBasisID());
553         Assertions.assertEquals("EME2000",                         att2.getMetadata().getEndpoints().getFrameA().getName());
554         Assertions.assertEquals(BaseEquipment.ACC,                 att2.getMetadata().getEndpoints().getFrameB().asSpacecraftBodyFrame().getBaseEquipment());
555         Assertions.assertEquals("0",                               att2.getMetadata().getEndpoints().getFrameB().asSpacecraftBodyFrame().getLabel());
556         Assertions.assertEquals(6,                                 att2.getMetadata().getNbStates());
557         Assertions.assertEquals(AttitudeElementsType.EULER_ANGLES, att2.getMetadata().getAttitudeType());
558         Assertions.assertEquals(RateElementsType.ANGVEL,           att2.getMetadata().getRateType());
559         Assertions.assertEquals(2,                                 att2.getAttitudeStates().size());
560         Assertions.assertEquals(0.50, att2.getAttitudeStates().get(0).getDate().durationFrom(acm.getMetadata().getEpochT0()), 1.0e-15);
561         Assertions.assertEquals(6,    att2.getAttitudeStates().get(0).getElements().length);
562         Assertions.assertEquals( 1.0, FastMath.toDegrees(att2.getAttitudeStates().get(0).getElements()[0]), 1.0e-15);
563         Assertions.assertEquals( 1.2, FastMath.toDegrees(att2.getAttitudeStates().get(0).getElements()[1]), 1.0e-15);
564         Assertions.assertEquals( 1.3, FastMath.toDegrees(att2.getAttitudeStates().get(0).getElements()[2]), 1.0e-15);
565         Assertions.assertEquals(-0.4, FastMath.toDegrees(att2.getAttitudeStates().get(0).getElements()[3]), 1.0e-15);
566         Assertions.assertEquals(-0.5, FastMath.toDegrees(att2.getAttitudeStates().get(0).getElements()[4]), 1.0e-15);
567         Assertions.assertEquals(-0.6, FastMath.toDegrees(att2.getAttitudeStates().get(0).getElements()[5]), 1.0e-15);
568         Assertions.assertEquals(0.75, att2.getAttitudeStates().get(1).getDate().durationFrom(acm.getMetadata().getEpochT0()), 1.0e-15);
569         Assertions.assertEquals(6,    att2.getAttitudeStates().get(1).getElements().length);
570         Assertions.assertEquals( 2.0, FastMath.toDegrees(att2.getAttitudeStates().get(1).getElements()[0]), 1.0e-15);
571         Assertions.assertEquals( 2.2, FastMath.toDegrees(att2.getAttitudeStates().get(1).getElements()[1]), 1.0e-15);
572         Assertions.assertEquals( 2.3, FastMath.toDegrees(att2.getAttitudeStates().get(1).getElements()[2]), 1.0e-15);
573         Assertions.assertEquals(-1.4, FastMath.toDegrees(att2.getAttitudeStates().get(1).getElements()[3]), 1.0e-15);
574         Assertions.assertEquals(-1.5, FastMath.toDegrees(att2.getAttitudeStates().get(1).getElements()[4]), 1.0e-15);
575         Assertions.assertEquals(-1.6, FastMath.toDegrees(att2.getAttitudeStates().get(1).getElements()[5]), 1.0e-15);
576         Assertions.assertEquals(0.0153152, att2.getAttitudeStates().get(0).toAngular(RotationOrder.XYZ).getRotationRate().getNorm(), 1.0e-7);
577 
578         // third attitude block
579         AttitudeStateHistory att3 = acm.getData().getAttitudeBlocks().get(2);
580         Assertions.assertEquals("third attitude block",           att3.getMetadata().getComments().get(0));
581         Assertions.assertEquals("ATT_3",                           att3.getMetadata().getAttID());
582         Assertions.assertEquals("ATT_2",                           att3.getMetadata().getAttPrevID());
583         Assertions.assertEquals("SIMULATED",                       att3.getMetadata().getAttBasis());
584         Assertions.assertEquals("rnd-25",                          att3.getMetadata().getAttBasisID());
585         Assertions.assertEquals("EME2000",                         att3.getMetadata().getEndpoints().getFrameA().getName());
586         Assertions.assertEquals(BaseEquipment.AST,                 att3.getMetadata().getEndpoints().getFrameB().asSpacecraftBodyFrame().getBaseEquipment());
587         Assertions.assertEquals("1",                               att3.getMetadata().getEndpoints().getFrameB().asSpacecraftBodyFrame().getLabel());
588         Assertions.assertEquals(8,                                 att3.getMetadata().getNbStates());
589         Assertions.assertEquals(AttitudeElementsType.QUATERNION,   att3.getMetadata().getAttitudeType());
590         Assertions.assertEquals(RateElementsType.Q_DOT,            att3.getMetadata().getRateType());
591         Assertions.assertEquals(2,                                 att3.getAttitudeStates().size());
592         Assertions.assertEquals(1.0,         att3.getAttitudeStates().get(0).getDate().durationFrom(acm.getMetadata().getEpochT0()), 1.0e-15);
593         Assertions.assertEquals(8,           att3.getAttitudeStates().get(0).getElements().length);
594         Assertions.assertEquals( 0.73566,    att3.getAttitudeStates().get(0).getElements()[0], 1.0e-15);
595         Assertions.assertEquals(-0.50547,    att3.getAttitudeStates().get(0).getElements()[1], 1.0e-15);
596         Assertions.assertEquals( 0.41309,    att3.getAttitudeStates().get(0).getElements()[2], 1.0e-15);
597         Assertions.assertEquals( 0.180707,   att3.getAttitudeStates().get(0).getElements()[3], 1.0e-15);
598         Assertions.assertEquals( 0.0073566,  att3.getAttitudeStates().get(0).getElements()[4], 1.0e-15);
599         Assertions.assertEquals(-0.0050547,  att3.getAttitudeStates().get(0).getElements()[5], 1.0e-15);
600         Assertions.assertEquals( 0.0041309,  att3.getAttitudeStates().get(0).getElements()[6], 1.0e-15);
601         Assertions.assertEquals( 0.00180707, att3.getAttitudeStates().get(0).getElements()[7], 1.0e-15);
602         Assertions.assertEquals(1.25,        att3.getAttitudeStates().get(1).getDate().durationFrom(acm.getMetadata().getEpochT0()), 1.0e-15);
603         Assertions.assertEquals(8,           att3.getAttitudeStates().get(1).getElements().length);
604         Assertions.assertEquals( 0.73529,    att3.getAttitudeStates().get(1).getElements()[0], 1.0e-15);
605         Assertions.assertEquals(-0.50531,    att3.getAttitudeStates().get(1).getElements()[1], 1.0e-15);
606         Assertions.assertEquals( 0.41375,    att3.getAttitudeStates().get(1).getElements()[2], 1.0e-15);
607         Assertions.assertEquals( 0.181158,   att3.getAttitudeStates().get(1).getElements()[3], 1.0e-15);
608         Assertions.assertEquals( 0.0073529,  att3.getAttitudeStates().get(1).getElements()[4], 1.0e-15);
609         Assertions.assertEquals(-0.0050531,  att3.getAttitudeStates().get(1).getElements()[5], 1.0e-15);
610         Assertions.assertEquals( 0.0041375,  att3.getAttitudeStates().get(1).getElements()[6], 1.0e-15);
611         Assertions.assertEquals( 0.00181158, att3.getAttitudeStates().get(1).getElements()[7], 1.0e-15);
612         Assertions.assertEquals(0.0, att3.getAttitudeStates().get(0).toAngular(RotationOrder.XYZ).getRotationRate().getNorm(), 1.0e-10);
613 
614         // fourth attitude block
615         AttitudeStateHistory att4 = acm.getData().getAttitudeBlocks().get(3);
616         Assertions.assertEquals("fourth attitude block",           att4.getMetadata().getComments().get(0));
617         Assertions.assertEquals("ATT_4",                           att4.getMetadata().getAttID());
618         Assertions.assertEquals("ATT_3",                           att4.getMetadata().getAttPrevID());
619         Assertions.assertEquals("SIMULATED",                       att4.getMetadata().getAttBasis());
620         Assertions.assertEquals("rnd-25",                          att4.getMetadata().getAttBasisID());
621         Assertions.assertEquals("EME2000",                         att4.getMetadata().getEndpoints().getFrameA().getName());
622         Assertions.assertEquals(BaseEquipment.CSS,                 att4.getMetadata().getEndpoints().getFrameB().asSpacecraftBodyFrame().getBaseEquipment());
623         Assertions.assertEquals("7",                               att4.getMetadata().getEndpoints().getFrameB().asSpacecraftBodyFrame().getLabel());
624         Assertions.assertEquals(13,                                att4.getMetadata().getNbStates());
625         Assertions.assertEquals(AttitudeElementsType.DCM,          att4.getMetadata().getAttitudeType());
626         Assertions.assertEquals(RateElementsType.Q_DOT,            att4.getMetadata().getRateType());
627         Assertions.assertEquals(2,                                 att4.getAttitudeStates().size());
628         Assertions.assertEquals(1.50, att4.getAttitudeStates().get(0).getDate().durationFrom(acm.getMetadata().getEpochT0()), 1.0e-15);
629         Assertions.assertEquals(13,   att4.getAttitudeStates().get(0).getElements().length);
630         Assertions.assertEquals(1.0,  att4.getAttitudeStates().get(0).getElements()[ 0], 1.0e-15);
631         Assertions.assertEquals(0.0,  att4.getAttitudeStates().get(0).getElements()[ 1], 1.0e-15);
632         Assertions.assertEquals(0.0,  att4.getAttitudeStates().get(0).getElements()[ 2], 1.0e-15);
633         Assertions.assertEquals(0.0,  att4.getAttitudeStates().get(0).getElements()[ 3], 1.0e-15);
634         Assertions.assertEquals(1.0,  att4.getAttitudeStates().get(0).getElements()[ 4], 1.0e-15);
635         Assertions.assertEquals(0.0,  att4.getAttitudeStates().get(0).getElements()[ 5], 1.0e-15);
636         Assertions.assertEquals(0.0,  att4.getAttitudeStates().get(0).getElements()[ 6], 1.0e-15);
637         Assertions.assertEquals(0.0,  att4.getAttitudeStates().get(0).getElements()[ 7], 1.0e-15);
638         Assertions.assertEquals(1.0,  att4.getAttitudeStates().get(0).getElements()[ 8], 1.0e-15);
639         Assertions.assertEquals(0.01, att4.getAttitudeStates().get(0).getElements()[ 9], 1.0e-15);
640         Assertions.assertEquals(0.02, att4.getAttitudeStates().get(0).getElements()[10], 1.0e-15);
641         Assertions.assertEquals(0.03, att4.getAttitudeStates().get(0).getElements()[11], 1.0e-15);
642         Assertions.assertEquals(0.04, att4.getAttitudeStates().get(0).getElements()[12], 1.0e-15);
643         Assertions.assertEquals(1.75, att4.getAttitudeStates().get(1).getDate().durationFrom(acm.getMetadata().getEpochT0()), 1.0e-15);
644         Assertions.assertEquals(13,   att4.getAttitudeStates().get(1).getElements().length);
645         Assertions.assertEquals(0.0,  att4.getAttitudeStates().get(1).getElements()[ 0], 1.0e-15);
646         Assertions.assertEquals(1.0,  att4.getAttitudeStates().get(1).getElements()[ 1], 1.0e-15);
647         Assertions.assertEquals(0.0,  att4.getAttitudeStates().get(1).getElements()[ 2], 1.0e-15);
648         Assertions.assertEquals(0.0,  att4.getAttitudeStates().get(1).getElements()[ 3], 1.0e-15);
649         Assertions.assertEquals(0.0,  att4.getAttitudeStates().get(1).getElements()[ 4], 1.0e-15);
650         Assertions.assertEquals(1.0,  att4.getAttitudeStates().get(1).getElements()[ 5], 1.0e-15);
651         Assertions.assertEquals(1.0,  att4.getAttitudeStates().get(1).getElements()[ 6], 1.0e-15);
652         Assertions.assertEquals(0.0,  att4.getAttitudeStates().get(1).getElements()[ 7], 1.0e-15);
653         Assertions.assertEquals(0.0,  att4.getAttitudeStates().get(1).getElements()[ 8], 1.0e-15);
654         Assertions.assertEquals(0.05, att4.getAttitudeStates().get(1).getElements()[ 9], 1.0e-15);
655         Assertions.assertEquals(0.06, att4.getAttitudeStates().get(1).getElements()[10], 1.0e-15);
656         Assertions.assertEquals(0.07, att4.getAttitudeStates().get(1).getElements()[11], 1.0e-15);
657         Assertions.assertEquals(0.08, att4.getAttitudeStates().get(1).getElements()[12], 1.0e-15);
658         Assertions.assertEquals(0.0748331, att4.getAttitudeStates().get(0).toAngular(RotationOrder.XYZ).getRotationRate().getNorm(), 1.0e-7);
659 
660         // fifth attitude block
661         AttitudeStateHistory att5 = acm.getData().getAttitudeBlocks().get(4);
662         Assertions.assertEquals("fifth attitude block",           att5.getMetadata().getComments().get(0));
663         Assertions.assertEquals("ATT_5",                           att5.getMetadata().getAttID());
664         Assertions.assertEquals("ATT_4",                           att5.getMetadata().getAttPrevID());
665         Assertions.assertEquals("SIMULATED",                       att5.getMetadata().getAttBasis());
666         Assertions.assertEquals("rnd-25",                          att5.getMetadata().getAttBasisID());
667         Assertions.assertEquals("EME2000",                         att5.getMetadata().getEndpoints().getFrameA().getName());
668         Assertions.assertEquals(BaseEquipment.ESA,                 att5.getMetadata().getEndpoints().getFrameB().asSpacecraftBodyFrame().getBaseEquipment());
669         Assertions.assertEquals("9",                               att5.getMetadata().getEndpoints().getFrameB().asSpacecraftBodyFrame().getLabel());
670         Assertions.assertEquals(6,                                 att5.getMetadata().getNbStates());
671         Assertions.assertEquals(AttitudeElementsType.EULER_ANGLES, att5.getMetadata().getAttitudeType());
672         Assertions.assertEquals(RateElementsType.GYRO_BIAS,        att5.getMetadata().getRateType());
673         Assertions.assertEquals(2,                                 att5.getAttitudeStates().size());
674         Assertions.assertEquals(2.00, att5.getAttitudeStates().get(0).getDate().durationFrom(acm.getMetadata().getEpochT0()), 1.0e-15);
675         Assertions.assertEquals(6,    att5.getAttitudeStates().get(0).getElements().length);
676         Assertions.assertEquals( 1.0, FastMath.toDegrees(att5.getAttitudeStates().get(0).getElements()[0]), 1.0e-15);
677         Assertions.assertEquals( 1.2, FastMath.toDegrees(att5.getAttitudeStates().get(0).getElements()[1]), 1.0e-15);
678         Assertions.assertEquals( 1.3, FastMath.toDegrees(att5.getAttitudeStates().get(0).getElements()[2]), 1.0e-15);
679         Assertions.assertEquals(-0.4, FastMath.toDegrees(att5.getAttitudeStates().get(0).getElements()[3]), 1.0e-15);
680         Assertions.assertEquals(-0.5, FastMath.toDegrees(att5.getAttitudeStates().get(0).getElements()[4]), 1.0e-15);
681         Assertions.assertEquals(-0.6, FastMath.toDegrees(att5.getAttitudeStates().get(0).getElements()[5]), 1.0e-15);
682         Assertions.assertEquals(2.25, att5.getAttitudeStates().get(1).getDate().durationFrom(acm.getMetadata().getEpochT0()), 1.0e-15);
683         Assertions.assertEquals(6,    att5.getAttitudeStates().get(1).getElements().length);
684         Assertions.assertEquals( 2.0, FastMath.toDegrees(att5.getAttitudeStates().get(1).getElements()[0]), 1.0e-15);
685         Assertions.assertEquals( 2.2, FastMath.toDegrees(att5.getAttitudeStates().get(1).getElements()[1]), 1.0e-15);
686         Assertions.assertEquals( 2.3, FastMath.toDegrees(att5.getAttitudeStates().get(1).getElements()[2]), 1.0e-15);
687         Assertions.assertEquals(-1.4, FastMath.toDegrees(att5.getAttitudeStates().get(1).getElements()[3]), 1.0e-15);
688         Assertions.assertEquals(-1.5, FastMath.toDegrees(att5.getAttitudeStates().get(1).getElements()[4]), 1.0e-15);
689         Assertions.assertEquals(-1.6, FastMath.toDegrees(att5.getAttitudeStates().get(1).getElements()[5]), 1.0e-15);
690         try {
691             att5.getAttitudeStates().get(0).toAngular(RotationOrder.XYZ);
692             Assertions.fail("an exception should have been thrown");
693         } catch (OrekitException oe) {
694             Assertions.assertEquals(OrekitMessages.CCSDS_UNSUPPORTED_ELEMENT_SET_TYPE, oe.getSpecifier());
695         }
696 
697         // physical properties
698         AttitudePhysicalProperties phys = acm.getData().getPhysicBlock();
699         Assertions.assertEquals("Spacecraft Physical Parameters", phys.getComments().get(0));
700         Assertions.assertEquals(1.8,    phys.getDragCoefficient(), 1.0e-15);
701         Assertions.assertEquals(1916.0, phys.getWetMass(), 1.0e-15);
702         Assertions.assertEquals(800.0,  phys.getDryMass(), 1.0e-15);
703         Assertions.assertEquals(1916.0, phys.getWetMass(), 1.0e-15);
704         Assertions.assertEquals(BaseEquipment.SC_BODY, phys.getCenterOfPressureReferenceFrame().asSpacecraftBodyFrame().getBaseEquipment());
705         Assertions.assertEquals("1", phys.getCenterOfPressureReferenceFrame().asSpacecraftBodyFrame().getLabel());
706         Assertions.assertEquals( 0.04,  phys.getCenterOfPressure().getX(),      1.0e-15);
707         Assertions.assertEquals(-0.78,  phys.getCenterOfPressure().getY(),      1.0e-15);
708         Assertions.assertEquals(-0.023, phys.getCenterOfPressure().getZ(),      1.0e-15);
709         Assertions.assertEquals(BaseEquipment.SC_BODY, phys.getInertiaReferenceFrame().asSpacecraftBodyFrame().getBaseEquipment());
710         Assertions.assertEquals("2", phys.getInertiaReferenceFrame().asSpacecraftBodyFrame().getLabel());
711         Assertions.assertEquals( 752.0, phys.getInertiaMatrix().getEntry(0, 0), 1.0e-15);
712         Assertions.assertEquals(1305.0, phys.getInertiaMatrix().getEntry(1, 1), 1.0e-15);
713         Assertions.assertEquals(1490.0, phys.getInertiaMatrix().getEntry(2, 2), 1.0e-15);
714         Assertions.assertEquals(  81.1, phys.getInertiaMatrix().getEntry(0, 1), 1.0e-15);
715         Assertions.assertEquals(  81.1, phys.getInertiaMatrix().getEntry(1, 0), 1.0e-15);
716         Assertions.assertEquals( -25.7, phys.getInertiaMatrix().getEntry(0, 2), 1.0e-15);
717         Assertions.assertEquals( -25.7, phys.getInertiaMatrix().getEntry(2, 0), 1.0e-15);
718         Assertions.assertEquals(  74.1, phys.getInertiaMatrix().getEntry(1, 2), 1.0e-15);
719         Assertions.assertEquals(  74.1, phys.getInertiaMatrix().getEntry(2, 1), 1.0e-15);
720 
721         Assertions.assertEquals(6, acm.getData().getCovarianceBlocks().size());
722 
723         // first covariance block
724         AttitudeCovarianceHistory cov1 = acm.getData().getCovarianceBlocks().get(0);
725         Assertions.assertEquals("first covariance block", cov1.getMetadata().getComments().get(0));
726         Assertions.assertEquals("COV_1", cov1.getMetadata().getCovID());
727         Assertions.assertEquals("COV_0", cov1.getMetadata().getCovPrevID());
728         Assertions.assertEquals("DETERMINED_OBC", cov1.getMetadata().getCovBasis());
729         Assertions.assertEquals("blip-12", cov1.getMetadata().getCovBasisID());
730         Assertions.assertEquals(BaseEquipment.SC_BODY, cov1.getMetadata().getCovReferenceFrame().asSpacecraftBodyFrame().getBaseEquipment());
731         Assertions.assertEquals("1", cov1.getMetadata().getCovReferenceFrame().asSpacecraftBodyFrame().getLabel());
732         Assertions.assertEquals(AttitudeCovarianceType.ANGLE_GYROBIAS, cov1.getMetadata().getCovType());
733         Assertions.assertEquals(2, cov1.getCovariances().size());
734         Assertions.assertEquals(0.0, cov1.getCovariances().get(0).getDate().durationFrom(acm.getMetadata().getEpochT0()), 1.0e-15);
735         DiagonalMatrix m1 = cov1.getCovariances().get(0).getMatrix();
736         Assertions.assertEquals(6,   m1.getRowDimension());
737         Assertions.assertEquals( 6.74E-11, FastMath.toDegrees(FastMath.toDegrees(m1.getEntry(0, 0))), 1.0e-22);
738         Assertions.assertEquals( 8.10E-11, FastMath.toDegrees(FastMath.toDegrees(m1.getEntry(1, 1))), 1.0e-22);
739         Assertions.assertEquals( 9.22E-11, FastMath.toDegrees(FastMath.toDegrees(m1.getEntry(2, 2))), 1.0e-22);
740         Assertions.assertEquals( 1.11E-15, FastMath.toDegrees(FastMath.toDegrees(m1.getEntry(3, 3))), 1.0e-22);
741         Assertions.assertEquals( 1.11E-15, FastMath.toDegrees(FastMath.toDegrees(m1.getEntry(4, 4))), 1.0e-22);
742         Assertions.assertEquals( 1.12E-15, FastMath.toDegrees(FastMath.toDegrees(m1.getEntry(5, 5))), 1.0e-22);
743 
744         // second covariance block
745         AttitudeCovarianceHistory cov2 = acm.getData().getCovarianceBlocks().get(1);
746         Assertions.assertEquals("second covariance block", cov2.getMetadata().getComments().get(0));
747         Assertions.assertEquals("COV_2", cov2.getMetadata().getCovID());
748         Assertions.assertEquals("COV_1", cov2.getMetadata().getCovPrevID());
749         Assertions.assertEquals("DETERMINED_OBC", cov2.getMetadata().getCovBasis());
750         Assertions.assertEquals("blip-12", cov2.getMetadata().getCovBasisID());
751         Assertions.assertEquals(BaseEquipment.SC_BODY, cov2.getMetadata().getCovReferenceFrame().asSpacecraftBodyFrame().getBaseEquipment());
752         Assertions.assertEquals("1", cov2.getMetadata().getCovReferenceFrame().asSpacecraftBodyFrame().getLabel());
753         Assertions.assertEquals(AttitudeCovarianceType.ANGLE, cov2.getMetadata().getCovType());
754         Assertions.assertEquals(2, cov2.getCovariances().size());
755         Assertions.assertEquals(0.0, cov2.getCovariances().get(0).getDate().durationFrom(acm.getMetadata().getEpochT0()), 1.0e-15);
756         DiagonalMatrix m2 = cov2.getCovariances().get(0).getMatrix();
757         Assertions.assertEquals(3,   m2.getRowDimension());
758         Assertions.assertEquals( 6.74E-11, FastMath.toDegrees(FastMath.toDegrees(m2.getEntry(0, 0))), 1.0e-22);
759         Assertions.assertEquals( 8.10E-11, FastMath.toDegrees(FastMath.toDegrees(m2.getEntry(1, 1))), 1.0e-22);
760         Assertions.assertEquals( 9.22E-11, FastMath.toDegrees(FastMath.toDegrees(m2.getEntry(2, 2))), 1.0e-22);
761 
762         // third covariance block
763         AttitudeCovarianceHistory cov3 = acm.getData().getCovarianceBlocks().get(2);
764         Assertions.assertEquals("third covariance block", cov3.getMetadata().getComments().get(0));
765         Assertions.assertEquals("COV_3", cov3.getMetadata().getCovID());
766         Assertions.assertEquals("COV_2", cov3.getMetadata().getCovPrevID());
767         Assertions.assertEquals("DETERMINED_OBC", cov3.getMetadata().getCovBasis());
768         Assertions.assertEquals("blip-12", cov3.getMetadata().getCovBasisID());
769         Assertions.assertEquals(BaseEquipment.SC_BODY, cov3.getMetadata().getCovReferenceFrame().asSpacecraftBodyFrame().getBaseEquipment());
770         Assertions.assertEquals("1", cov3.getMetadata().getCovReferenceFrame().asSpacecraftBodyFrame().getLabel());
771         Assertions.assertEquals(AttitudeCovarianceType.ANGLE_ANGVEL, cov3.getMetadata().getCovType());
772         Assertions.assertEquals(2, cov3.getCovariances().size());
773         Assertions.assertEquals(0.0, cov3.getCovariances().get(0).getDate().durationFrom(acm.getMetadata().getEpochT0()), 1.0e-15);
774         DiagonalMatrix m3 = cov3.getCovariances().get(0).getMatrix();
775         Assertions.assertEquals(6,   m3.getRowDimension());
776         Assertions.assertEquals( 6.74E-11, FastMath.toDegrees(FastMath.toDegrees(m3.getEntry(0, 0))), 1.0e-22);
777         Assertions.assertEquals( 8.10E-11, FastMath.toDegrees(FastMath.toDegrees(m3.getEntry(1, 1))), 1.0e-22);
778         Assertions.assertEquals( 9.22E-11, FastMath.toDegrees(FastMath.toDegrees(m3.getEntry(2, 2))), 1.0e-22);
779         Assertions.assertEquals( 1.11E-15, FastMath.toDegrees(FastMath.toDegrees(m3.getEntry(3, 3))), 1.0e-22);
780         Assertions.assertEquals( 1.11E-15, FastMath.toDegrees(FastMath.toDegrees(m3.getEntry(4, 4))), 1.0e-22);
781         Assertions.assertEquals( 1.12E-15, FastMath.toDegrees(FastMath.toDegrees(m3.getEntry(5, 5))), 1.0e-22);
782 
783         // fourth covariance block
784         AttitudeCovarianceHistory cov4 = acm.getData().getCovarianceBlocks().get(3);
785         Assertions.assertEquals("fourth covariance block", cov4.getMetadata().getComments().get(0));
786         Assertions.assertEquals("COV_4", cov4.getMetadata().getCovID());
787         Assertions.assertEquals("COV_3", cov4.getMetadata().getCovPrevID());
788         Assertions.assertEquals("DETERMINED_OBC", cov4.getMetadata().getCovBasis());
789         Assertions.assertEquals("blip-12", cov4.getMetadata().getCovBasisID());
790         Assertions.assertEquals(BaseEquipment.SC_BODY, cov4.getMetadata().getCovReferenceFrame().asSpacecraftBodyFrame().getBaseEquipment());
791         Assertions.assertEquals("1", cov4.getMetadata().getCovReferenceFrame().asSpacecraftBodyFrame().getLabel());
792         Assertions.assertEquals(AttitudeCovarianceType.QUATERNION, cov4.getMetadata().getCovType());
793         Assertions.assertEquals(2, cov4.getCovariances().size());
794         Assertions.assertEquals(0.0, cov4.getCovariances().get(0).getDate().durationFrom(acm.getMetadata().getEpochT0()), 1.0e-15);
795         DiagonalMatrix m4 = cov4.getCovariances().get(0).getMatrix();
796         Assertions.assertEquals(4,   m4.getRowDimension());
797         Assertions.assertEquals( 6.74E-11, m4.getEntry(0, 0), 1.0e-22);
798         Assertions.assertEquals( 8.10E-11, m4.getEntry(1, 1), 1.0e-22);
799         Assertions.assertEquals( 9.22E-11, m4.getEntry(2, 2), 1.0e-22);
800         Assertions.assertEquals( 2.17E-11, m4.getEntry(3, 3), 1.0e-22);
801 
802         // fifth covariance block
803         AttitudeCovarianceHistory cov5 = acm.getData().getCovarianceBlocks().get(4);
804         Assertions.assertEquals("fifth covariance block", cov5.getMetadata().getComments().get(0));
805         Assertions.assertEquals("COV_5", cov5.getMetadata().getCovID());
806         Assertions.assertEquals("COV_4", cov5.getMetadata().getCovPrevID());
807         Assertions.assertEquals("DETERMINED_OBC", cov5.getMetadata().getCovBasis());
808         Assertions.assertEquals("blip-12", cov5.getMetadata().getCovBasisID());
809         Assertions.assertEquals(BaseEquipment.SC_BODY, cov5.getMetadata().getCovReferenceFrame().asSpacecraftBodyFrame().getBaseEquipment());
810         Assertions.assertEquals("1", cov5.getMetadata().getCovReferenceFrame().asSpacecraftBodyFrame().getLabel());
811         Assertions.assertEquals(AttitudeCovarianceType.QUATERNION_GYROBIAS, cov5.getMetadata().getCovType());
812         Assertions.assertEquals(2, cov5.getCovariances().size());
813         Assertions.assertEquals(0.0, cov5.getCovariances().get(0).getDate().durationFrom(acm.getMetadata().getEpochT0()), 1.0e-15);
814         DiagonalMatrix m5 = cov5.getCovariances().get(0).getMatrix();
815         Assertions.assertEquals(7,   m5.getRowDimension());
816         Assertions.assertEquals( 6.74E-11, m5.getEntry(0, 0), 1.0e-22);
817         Assertions.assertEquals( 8.10E-11, m5.getEntry(1, 1), 1.0e-22);
818         Assertions.assertEquals( 9.22E-11, m5.getEntry(2, 2), 1.0e-22);
819         Assertions.assertEquals( 2.17E-11, m5.getEntry(3, 3), 1.0e-22);
820         Assertions.assertEquals( 1.11E-15, FastMath.toDegrees(FastMath.toDegrees(m5.getEntry(4, 4))), 1.0e-22);
821         Assertions.assertEquals( 1.11E-15, FastMath.toDegrees(FastMath.toDegrees(m5.getEntry(5, 5))), 1.0e-22);
822         Assertions.assertEquals( 1.12E-15, FastMath.toDegrees(FastMath.toDegrees(m5.getEntry(6, 6))), 1.0e-22);
823 
824         // sixth covariance block
825         AttitudeCovarianceHistory cov6 = acm.getData().getCovarianceBlocks().get(5);
826         Assertions.assertEquals("sixth covariance block", cov6.getMetadata().getComments().get(0));
827         Assertions.assertEquals("COV_6", cov6.getMetadata().getCovID());
828         Assertions.assertEquals("COV_5", cov6.getMetadata().getCovPrevID());
829         Assertions.assertEquals("DETERMINED_OBC", cov6.getMetadata().getCovBasis());
830         Assertions.assertEquals("blip-12", cov6.getMetadata().getCovBasisID());
831         Assertions.assertEquals(BaseEquipment.SC_BODY, cov6.getMetadata().getCovReferenceFrame().asSpacecraftBodyFrame().getBaseEquipment());
832         Assertions.assertEquals("1", cov6.getMetadata().getCovReferenceFrame().asSpacecraftBodyFrame().getLabel());
833         Assertions.assertEquals(AttitudeCovarianceType.QUATERNION_ANGVEL, cov6.getMetadata().getCovType());
834         Assertions.assertEquals(2, cov6.getCovariances().size());
835         Assertions.assertEquals(0.0, cov6.getCovariances().get(0).getDate().durationFrom(acm.getMetadata().getEpochT0()), 1.0e-15);
836         DiagonalMatrix m6 = cov6.getCovariances().get(0).getMatrix();
837         Assertions.assertEquals(7,   m6.getRowDimension());
838         Assertions.assertEquals( 6.74E-11, m6.getEntry(0, 0), 1.0e-22);
839         Assertions.assertEquals( 8.10E-11, m6.getEntry(1, 1), 1.0e-22);
840         Assertions.assertEquals( 9.22E-11, m6.getEntry(2, 2), 1.0e-22);
841         Assertions.assertEquals( 2.17E-11, m6.getEntry(3, 3), 1.0e-22);
842         Assertions.assertEquals( 1.11E-15, FastMath.toDegrees(FastMath.toDegrees(m6.getEntry(4, 4))), 1.0e-22);
843         Assertions.assertEquals( 1.11E-15, FastMath.toDegrees(FastMath.toDegrees(m6.getEntry(5, 5))), 1.0e-22);
844         Assertions.assertEquals( 1.12E-15, FastMath.toDegrees(FastMath.toDegrees(m6.getEntry(6, 6))), 1.0e-22);
845 
846         Assertions.assertEquals(3, acm.getData().getManeuverBlocks().size());
847 
848         // first maneuver
849         AttitudeManeuver man1 = acm.getData().getManeuverBlocks().get(0);
850         Assertions.assertEquals("first maneuver",               man1.getComments().get(0));
851         Assertions.assertEquals("MAN_1",                        man1.getID());
852         Assertions.assertEquals("MAN_0",                        man1.getPrevID());
853         Assertions.assertEquals("MOM_DESAT",                    man1.getManPurpose());
854         Assertions.assertEquals(100.0,                          man1.getBeginTime());
855         Assertions.assertTrue(Double.isNaN(man1.getEndTime()));
856         Assertions.assertEquals(450.0,                          man1.getDuration());
857         Assertions.assertEquals("ATT-THRUSTER",                 man1.getActuatorUsed());
858         Assertions.assertEquals(  1.3,                          man1.getTargetMomentum().getX(), 1.0e-10);
859         Assertions.assertEquals(-16.4,                          man1.getTargetMomentum().getY(), 1.0e-10);
860         Assertions.assertEquals(-11.35,                         man1.getTargetMomentum().getZ(), 1.0e-10);
861         Assertions.assertNull(man1.getTargetAttitude());
862         Assertions.assertTrue(Double.isNaN(man1.getTargetSpinRate()));
863 
864         // second maneuver
865         AttitudeManeuver man2 = acm.getData().getManeuverBlocks().get(1);
866         Assertions.assertEquals("second maneuver",              man2.getComments().get(0));
867         Assertions.assertEquals("MAN_2",                        man2.getID());
868         Assertions.assertEquals("MAN_1",                        man2.getPrevID());
869         Assertions.assertEquals("ATT_ADJUST",                   man2.getManPurpose());
870         Assertions.assertEquals(500.0,                          man2.getBeginTime());
871         Assertions.assertEquals(600.0,                          man2.getEndTime());
872         Assertions.assertTrue(Double.isNaN(man2.getDuration()));
873         Assertions.assertEquals("MAGNETOTORQUER",               man2.getActuatorUsed());
874         Assertions.assertNull(man2.getTargetMomentum());
875         Assertions.assertEquals(  0.0,                          man2.getTargetAttitude().getQ1(), 1.0e-10);
876         Assertions.assertEquals(  0.0,                          man2.getTargetAttitude().getQ2(), 1.0e-10);
877         Assertions.assertEquals(-0.707106781187,                man2.getTargetAttitude().getQ3(), 1.0e-10);
878         Assertions.assertEquals( 0.707106781187,                man2.getTargetAttitude().getQ0(), 1.0e-10);
879         Assertions.assertTrue(Double.isNaN(man2.getTargetSpinRate()));
880 
881         // third maneuver
882         AttitudeManeuver man3 = acm.getData().getManeuverBlocks().get(2);
883         Assertions.assertEquals("third maneuver",               man3.getComments().get(0));
884         Assertions.assertEquals("MAN_3",                        man3.getID());
885         Assertions.assertEquals("MAN_2",                        man3.getPrevID());
886         Assertions.assertEquals("SPIN_RATE_ADJUST",             man3.getManPurpose());
887         Assertions.assertEquals(700.0,                          man3.getBeginTime());
888         Assertions.assertTrue(Double.isNaN(man3.getEndTime()));
889         Assertions.assertEquals(200.0,                          man3.getDuration());
890         Assertions.assertEquals("REACTION-WHEEL",               man3.getActuatorUsed());
891         Assertions.assertNull(man3.getTargetMomentum());
892         Assertions.assertNull(man3.getTargetAttitude());
893         Assertions.assertEquals( 0.12,                          FastMath.toDegrees(man3.getTargetSpinRate()), 1.0e-10);
894 
895         // attitude determi nation
896         AttitudeDetermination ad = acm.getData().getAttitudeDeterminationBlock();
897         Assertions.assertEquals("attitude determination block",        ad.getComments().get(0));
898         Assertions.assertEquals("AD_1",                                ad.getId());
899         Assertions.assertEquals("AD_0",                                ad.getPrevId());
900         Assertions.assertEquals(AdMethodType.Q_METHOD,                 ad.getMethod());
901         Assertions.assertEquals("OBC",                                 ad.getSource());
902         Assertions.assertEquals(RotationOrder.XYZ,                     ad.getEulerRotSeq());
903         Assertions.assertEquals(7,                                     ad.getNbStates());
904         Assertions.assertEquals(AttitudeElementsType.QUATERNION,       ad.getAttitudeStates());
905         Assertions.assertEquals(AttitudeCovarianceType.ANGLE_GYROBIAS, ad.getCovarianceType());
906         Assertions.assertEquals("EME2000",                             ad.getEndpoints().getFrameA().getName());
907         Assertions.assertEquals(BaseEquipment.SC_BODY,                 ad.getEndpoints().getFrameB().asSpacecraftBodyFrame().getBaseEquipment());
908         Assertions.assertEquals("1",                                   ad.getEndpoints().getFrameB().asSpacecraftBodyFrame().getLabel());
909         Assertions.assertEquals(RateElementsType.ANGVEL,               ad.getRateStates());
910         Assertions.assertEquals(3,                                     ad.getSensorsUsed().size());
911         Assertions.assertEquals(1,                                     ad.getSensorsUsed().get(0).getSensorNumber());
912         Assertions.assertEquals("AST1",                                ad.getSensorsUsed().get(0).getSensorUsed());
913         Assertions.assertEquals( 2,                                    ad.getSensorsUsed().get(0).getNbSensorNoiseCovariance());
914         Assertions.assertEquals(0.0097, FastMath.toDegrees(ad.getSensorsUsed().get(0).getSensorNoiseCovariance()[0]), 1.0e-10);
915         Assertions.assertEquals(0.0098, FastMath.toDegrees(ad.getSensorsUsed().get(0).getSensorNoiseCovariance()[1]), 1.0e-10);
916         Assertions.assertEquals( 5.0, ad.getSensorsUsed().get(0).getSensorFrequency(), 1.0e-10);
917         Assertions.assertEquals(2,                                     ad.getSensorsUsed().get(1).getSensorNumber());
918         Assertions.assertEquals("AST2",                                ad.getSensorsUsed().get(1).getSensorUsed());
919         Assertions.assertEquals( 2,                                    ad.getSensorsUsed().get(1).getNbSensorNoiseCovariance());
920         Assertions.assertEquals(0.0079, FastMath.toDegrees(ad.getSensorsUsed().get(1).getSensorNoiseCovariance()[0]), 1.0e-10);
921         Assertions.assertEquals(0.0089, FastMath.toDegrees(ad.getSensorsUsed().get(1).getSensorNoiseCovariance()[1]), 1.0e-10);
922         Assertions.assertEquals(10.0, ad.getSensorsUsed().get(1).getSensorFrequency(), 1.0e-10);
923         Assertions.assertEquals(3,                                     ad.getSensorsUsed().get(2).getSensorNumber());
924         Assertions.assertEquals("IMU",                                 ad.getSensorsUsed().get(2).getSensorUsed());
925         Assertions.assertEquals(-1,                                    ad.getSensorsUsed().get(2).getNbSensorNoiseCovariance());
926         Assertions.assertNull(ad.getSensorsUsed().get(2).getSensorNoiseCovariance());
927         Assertions.assertTrue(Double.isNaN(ad.getSensorsUsed().get(2).getSensorFrequency()));
928 
929         Assertions.assertEquals(1, acm.getData().getUserDefinedBlock().getParameters().size());
930         Assertions.assertEquals("viscum-album", acm.getData().getUserDefinedBlock().getParameters().get("OXIDIZER"));
931 
932     }
933 
934     @Test
935     public void testWriteACM05() throws IOException {
936         final String name = "/ccsds/adm/acm/ACMExample05.txt";
937         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
938         AcmParser parser = new ParserBuilder().buildAcmParser();
939         final Acm original = parser.parseMessage(source);
940 
941         // write the parsed file back to a characters array
942         final CharArrayWriter caw = new CharArrayWriter();
943         final Generator generator = new XmlGenerator(caw, 2, "dummy", Constants.JULIAN_DAY, true, null);
944         new WriterBuilder().buildAcmWriter().writeMessage(generator, original);
945 
946         // reparse the written file
947         final byte[]     bytes   = caw.toString().getBytes(StandardCharsets.UTF_8);
948         final DataSource source2 = new DataSource(name, () -> new ByteArrayInputStream(bytes));
949         final Acm    rebuilt = new ParserBuilder().buildAcmParser().parseMessage(source2);
950         validateAcm05(rebuilt);
951 
952     }
953 
954     @Test
955     public void testFrameMapper() {
956         // setup
957         TimeScale tai = TimeScalesFactory.getTAI();
958         AbsoluteDate expectedEpoch = new AbsoluteDate("2016-03-15T00:00:00.0", tai);
959 
960         Frame parent = FramesFactory.getEME2000();
961         Frame myJ2000 = new Frame(parent, Transform.IDENTITY, "MyJ2000");
962         Frame scBodyFrame = new Frame(parent, new Transform(expectedEpoch, new Rotation(RotationOrder.XYZ, RotationConvention.FRAME_TRANSFORM,
963                 Math.PI / 4, Math.PI / 2, Math.PI / 3)), "SC_BODY_1");
964         Frame scBodyFrame2 = new Frame(parent, new Transform(expectedEpoch, new Rotation(RotationOrder.ZXZ, RotationConvention.FRAME_TRANSFORM,
965                 Math.PI / 4, Math.PI / 2, Math.PI / 3)), "SC_BODY_1");
966         Frame gyro3 = new Frame(parent, new Transform(expectedEpoch, new Rotation(RotationOrder.XYZ, RotationConvention.FRAME_TRANSFORM,
967                 Math.PI / 3, Math.PI / 4, Math.PI / 2)), "GYRO_3");
968         Frame acc0 = new Frame(parent, new Transform(expectedEpoch, new Rotation(RotationOrder.XYZ, RotationConvention.FRAME_TRANSFORM,
969                 Math.PI / 2, Math.PI / 3, Math.PI / 4)), "ACC_0");
970         Frame ast1 = new Frame(parent, new Transform(expectedEpoch, new Rotation(RotationOrder.ZXZ, RotationConvention.FRAME_TRANSFORM,
971                 Math.PI / 2, Math.PI / 3, Math.PI / 4)), "AST_1");
972         Frame css7 = new Frame(parent, new Transform(expectedEpoch, new Rotation(RotationOrder.ZXZ, RotationConvention.FRAME_TRANSFORM,
973                 Math.PI / 4, Math.PI / 3, Math.PI / 2)), "CSS_7");
974         Frame esa9 = new Frame(parent, new Transform(expectedEpoch, new Rotation(RotationOrder.ZXZ, RotationConvention.FRAME_TRANSFORM,
975                 Math.PI / 3, Math.PI / 2, Math.PI / 4)), "ESA_9_0");
976 
977         CcsdsFrameMapper mapper = new CcsdsFrameMapper() {
978             // Dummy override here, not calling this signature with the below test case
979             @Override
980             public Frame buildCcsdsFrame(FrameFacade orientation, AbsoluteDate epoch) {
981                 if (epoch != null) {
982                     throw new IllegalArgumentException("" + epoch);
983                 }
984                 if ("SC_BODY_1".equals(orientation.getName())) {
985                     return scBodyFrame;
986                 } else if ("EME2000".equals(orientation.getName())) {
987                     return myJ2000;
988                 } else {
989                     throw new IllegalArgumentException(orientation + " " + epoch);
990                 }
991             }
992 
993             @Override
994             public Frame buildCcsdsFrame(BodyFacade center,
995                                          FrameFacade orientation,
996                                          AbsoluteDate epoch) {
997                 if (epoch != null) {
998                     throw new IllegalArgumentException("" + epoch);
999                 }
1000                 if ("ZZ".equals(center.getName())) {
1001                     if ("SC_BODY_1".equals(orientation.getName())) {
1002                         return scBodyFrame;
1003                     } else if ("SC_BODY_2".equals(orientation.getName())) {
1004                         return scBodyFrame2;
1005                     } else if ("EME2000".equals(orientation.getName()) || "J2000".equals(orientation.getName())) {
1006                         return myJ2000;
1007                     } else if ("GYRO_3".equals(orientation.getName())) {
1008                         return gyro3;
1009                     } else if ("ACC_0".equals(orientation.getName())) {
1010                         return acc0;
1011                     } else if ("AST_1".equals(orientation.getName())) {
1012                         return ast1;
1013                     } else if ("CSS_7".equals(orientation.getName())) {
1014                         return css7;
1015                     } else if ("ESA_9".equals(orientation.getName())) {
1016                         return esa9;
1017                     } else {
1018                         throw new IllegalArgumentException(center + " " + orientation + " " + epoch);
1019                     }
1020                 } else {
1021                     throw new IllegalArgumentException(
1022                             center + " " + orientation + " " + epoch);
1023                 }
1024             }
1025 
1026         };
1027         final String name = "/ccsds/adm/acm/ACM-frame-mapper.txt";
1028         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
1029 
1030         // action
1031         final AcmParser parser = new ParserBuilder().withFrameMapper(mapper).buildAcmParser();
1032         final Acm acm = parser.parseMessage(source);
1033 
1034         // verify
1035         // blocks: attitude, physical properties, covariance, maneuvers, attitude determination
1036         final AcmData data = acm.getData();
1037         final BodyFacade center = acm.getMetadata().getCenter();
1038         final List<AttitudeStateHistory> attitudes = data.getAttitudeBlocks();
1039         final AttitudePhysicalProperties properties = data.getPhysicBlock();
1040         final List<AttitudeCovarianceHistory> covariances = data.getCovarianceBlocks();
1041         final List<AttitudeManeuver> maneuvers = data.getManeuverBlocks();
1042         final AttitudeDetermination attitudeDetermination = data.getAttitudeDeterminationBlock();
1043 
1044         // Attitude
1045         for (final AttitudeStateHistory attitudeStateHistory : attitudes) {
1046             // FrameFacade.asFrame() subverts mapping
1047             MatcherAssert.assertThat(attitudeStateHistory.getReferenceFrame(),
1048                     Matchers.sameInstance(myJ2000));
1049 
1050             // REF_FRAME_B mapping for each attitude state history
1051             MatcherAssert.assertThat(
1052                     attitudeStateHistory.getMetadata().getEndpoints().getExternal(),
1053                     Matchers.sameInstance(myJ2000));
1054         }
1055 
1056         // Physical properties
1057         MatcherAssert.assertThat(mapper.buildCcsdsFrame(center, properties.getCenterOfPressureReferenceFrame(), null),
1058                 Matchers.sameInstance(scBodyFrame)); // center of pressure frame
1059         MatcherAssert.assertThat(mapper.buildCcsdsFrame(center, properties.getInertiaReferenceFrame(), null),
1060                             Matchers.sameInstance(scBodyFrame2)); // inertia reference frame
1061 
1062         // Covariance
1063         covariances.forEach(covariance -> MatcherAssert.assertThat(
1064                             mapper.buildCcsdsFrame(center, covariance.getMetadata().getCovReferenceFrame(), null),
1065                             Matchers.sameInstance(scBodyFrame)));
1066 
1067         // Maneuvers
1068         maneuvers.stream().filter(maneuver -> maneuver.getTargetMomFrame() != null).
1069                     forEach(maneuver -> MatcherAssert.assertThat(
1070                             mapper.buildCcsdsFrame(center, maneuver.getTargetMomFrame(), null),
1071                             Matchers.sameInstance(myJ2000))); // target momentum frame
1072 
1073         // Attitude Determination
1074         MatcherAssert.assertThat(
1075                 attitudeDetermination.getEndpoints().getExternal(),
1076                 Matchers.sameInstance(myJ2000));
1077     }
1078 
1079     /** Test deprecated constructor. Can be removed in 14.0. */
1080     @Test
1081     @Deprecated
1082     public void testDeprecatedConstructor() {
1083         // action
1084         AcmParser actual = new AcmParser(
1085                 null, true, null, null, new Function[0]);
1086 
1087         // verify
1088         MatcherAssert.assertThat(actual.getFrameMapper(),
1089                 Matchers.is(new OrekitCcsdsFrameMapper()));
1090     }
1091 
1092 }