1   /* Copyright 2002-2025 CS GROUP
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.odm.ocm;
18  
19  import java.io.ByteArrayInputStream;
20  import java.io.CharArrayWriter;
21  import java.io.IOException;
22  import java.io.InputStreamReader;
23  import java.nio.charset.StandardCharsets;
24  import java.util.List;
25  import java.util.Locale;
26  import java.util.function.Function;
27  
28  import org.hamcrest.MatcherAssert;
29  import org.hamcrest.Matchers;
30  import org.hipparchus.geometry.euclidean.threed.Vector3D;
31  import org.hipparchus.util.FastMath;
32  import org.junit.jupiter.api.Assertions;
33  import org.junit.jupiter.api.BeforeEach;
34  import org.junit.jupiter.api.Test;
35  import org.orekit.Utils;
36  import org.orekit.data.DataContext;
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.BodyFacade;
41  import org.orekit.files.ccsds.definitions.CcsdsFrameMapper;
42  import org.orekit.files.ccsds.definitions.CelestialBodyFrame;
43  import org.orekit.files.ccsds.definitions.CenterName;
44  import org.orekit.files.ccsds.definitions.DutyCycleType;
45  import org.orekit.files.ccsds.definitions.FrameFacade;
46  import org.orekit.files.ccsds.definitions.OdMethodType;
47  import org.orekit.files.ccsds.definitions.OnOff;
48  import org.orekit.files.ccsds.definitions.OrbitRelativeFrame;
49  import org.orekit.files.ccsds.definitions.OrekitCcsdsFrameMapper;
50  import org.orekit.files.ccsds.definitions.SpacecraftBodyFrame;
51  import org.orekit.files.ccsds.definitions.TimeSystem;
52  import org.orekit.files.ccsds.definitions.Units;
53  import org.orekit.files.ccsds.ndm.ParserBuilder;
54  import org.orekit.files.ccsds.ndm.WriterBuilder;
55  import org.orekit.files.ccsds.ndm.odm.UserDefined;
56  import org.orekit.files.ccsds.ndm.odm.oem.InterpolationMethod;
57  import org.orekit.files.ccsds.utils.generation.Generator;
58  import org.orekit.files.ccsds.utils.generation.KvnGenerator;
59  import org.orekit.files.ccsds.utils.lexical.KvnLexicalAnalyzer;
60  import org.orekit.files.ccsds.utils.lexical.XmlLexicalAnalyzer;
61  import org.orekit.frames.Frame;
62  import org.orekit.frames.FramesFactory;
63  import org.orekit.frames.Transform;
64  import org.orekit.time.AbsoluteDate;
65  import org.orekit.time.TimeOffset;
66  import org.orekit.time.TimeScale;
67  import org.orekit.time.TimeScalesFactory;
68  import org.orekit.utils.Constants;
69  import org.orekit.utils.IERSConventions;
70  import org.orekit.utils.TimeStampedPVCoordinates;
71  import org.orekit.utils.units.Unit;
72  
73  public class OcmParserTest {
74  
75      @BeforeEach
76      public void setUp() {
77          Utils.setDataRoot("regular-data");
78      }
79  
80      @Test
81      public void testNonExistentKvnFile() {
82          final String realName = "/ccsds/odm/ocm/OCMExample1.txt";
83          final String wrongName = realName + "xxxxx";
84          final DataSource source = new DataSource(wrongName, () -> getClass().getResourceAsStream(wrongName));
85          try {
86              new KvnLexicalAnalyzer(source).accept(new ParserBuilder().buildOcmParser());
87              Assertions.fail("an exception should have been thrown");
88          } catch (OrekitException oe) {
89              Assertions.assertEquals(OrekitMessages.UNABLE_TO_FIND_FILE, oe.getSpecifier());
90              Assertions.assertEquals(wrongName, oe.getParts()[0]);
91          }
92      }
93  
94      @Test
95      public void testNonExistentXmlFile() {
96          final String realName = "/ccsds/odm/ocm/OCMExample1.txt";
97          final String wrongName = realName + "xxxxx";
98          final DataSource source = new DataSource(wrongName, () -> getClass().getResourceAsStream(wrongName));
99          try {
100             new XmlLexicalAnalyzer(source).accept(new ParserBuilder().buildOcmParser());
101             Assertions.fail("an exception should have been thrown");
102         } catch (OrekitException oe) {
103             Assertions.assertEquals(OrekitMessages.UNABLE_TO_FIND_FILE, oe.getSpecifier());
104             Assertions.assertEquals(wrongName, oe.getParts()[0]);
105         }
106     }
107 
108     @Test
109     public void testMissingT0() {
110         final String name = "/ccsds/odm/ocm/OCM-missing-t0.txt";
111         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
112         try {
113             new ParserBuilder().
114             withMu(Constants.EIGEN5C_EARTH_MU).
115             buildOcmParser().
116             parseMessage(source);
117             Assertions.fail("an exception should have been thrown");
118         } catch (OrekitException oe) {
119             Assertions.assertEquals(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, oe.getSpecifier());
120             Assertions.assertEquals(OcmMetadataKey.EPOCH_TZERO.name(), oe.getParts()[0]);
121         }
122     }
123 
124     @Test
125     public void testMissingManeuverTime() {
126         final String name = "/ccsds/odm/ocm/OCM-missing-maneuver-time.txt";
127         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
128         try {
129             new ParserBuilder().
130             withMu(Constants.EIGEN5C_EARTH_MU).
131             buildOcmParser().
132             parseMessage(source);
133             Assertions.fail("an exception should have been thrown");
134         } catch (OrekitException oe) {
135             Assertions.assertEquals(OrekitMessages.CCSDS_MANEUVER_MISSING_TIME, oe.getSpecifier());
136             Assertions.assertEquals("MAN-45", oe.getParts()[0]);
137         }
138     }
139 
140     @Test
141     public void testWrongTimeSpan() {
142         final String name = "/ccsds/odm/ocm/OCM-wrong-time-span.txt";
143         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
144         try {
145             new ParserBuilder().
146             withMu(Constants.EIGEN5C_EARTH_MU).
147             buildOcmParser().
148             parseMessage(source);
149             Assertions.fail("an exception should have been thrown");
150         } catch (OrekitException oe) {
151             Assertions.assertEquals(OrekitMessages.UNABLE_TO_PARSE_ELEMENT_IN_FILE, oe.getSpecifier());
152             Assertions.assertEquals("TIME_SPAN", oe.getParts()[0]);
153             Assertions.assertEquals(11, ((Integer) oe.getParts()[1]).intValue());
154             Assertions.assertTrue(((String) oe.getParts()[2]).endsWith("OCM-wrong-time-span.txt"));
155         }
156     }
157 
158     @Test
159     public void testMissingRevnumBasis() {
160         final String name = "/ccsds/odm/ocm/OCM-missing-revnum-basis.txt";
161         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
162         try {
163             new ParserBuilder().
164             withMu(Constants.EIGEN5C_EARTH_MU).
165             buildOcmParser().
166             parseMessage(source);
167             Assertions.fail("an exception should have been thrown");
168         } catch (OrekitException oe) {
169             Assertions.assertEquals(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, oe.getSpecifier());
170             Assertions.assertEquals(TrajectoryStateHistoryMetadataKey.ORB_REVNUM_BASIS.name(), oe.getParts()[0]);
171         }
172     }
173 
174     @Test
175     public void testSpuriousMetaDataSection() {
176         final String name = "/ccsds/odm/ocm/OCM-spurious-metadata-section.txt";
177         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
178         try {
179             new ParserBuilder().
180             withMu(Constants.EIGEN5C_EARTH_MU).
181             buildOcmParser().
182             parseMessage(source);
183             Assertions.fail("an exception should have been thrown");
184         } catch (OrekitException oe) {
185             Assertions.assertEquals(OrekitMessages.CCSDS_UNEXPECTED_KEYWORD, oe.getSpecifier());
186             Assertions.assertEquals(13, ((Integer) oe.getParts()[0]).intValue());
187             Assertions.assertEquals("META", oe.getParts()[2]);
188         }
189     }
190 
191     @Test
192     public void testIncompatibleUnitsDimension() {
193         final String name = "/ccsds/odm/ocm/OCM-incompatible-units-dimension.txt";
194         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
195         try {
196             new ParserBuilder().
197             withMu(Constants.EIGEN5C_EARTH_MU).
198             buildOcmParser().
199             parseMessage(source);
200             Assertions.fail("an exception should have been thrown");
201         } catch (OrekitException oe) {
202             Assertions.assertEquals(OrekitMessages.INCOMPATIBLE_UNITS, oe.getSpecifier());
203             Assertions.assertEquals("km²/s", oe.getParts()[0]);
204             Assertions.assertEquals("m", oe.getParts()[1]);
205         }
206     }
207 
208     @Test
209     public void testIncompatibleUnitsScale() {
210         final String name = "/ccsds/odm/ocm/OCM-incompatible-units-scale.txt";
211         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
212         try {
213             new ParserBuilder().
214             withMu(Constants.EIGEN5C_EARTH_MU).
215             buildOcmParser().
216             parseMessage(source);
217             Assertions.fail("an exception should have been thrown");
218         } catch (OrekitException oe) {
219             Assertions.assertEquals(OrekitMessages.INCOMPATIBLE_UNITS, oe.getSpecifier());
220             Assertions.assertEquals("km", oe.getParts()[0]);
221             Assertions.assertEquals("m", oe.getParts()[1]);
222         }
223     }
224 
225     @Test
226     public void testWrongNbElements() {
227         final String name = "/ccsds/odm/ocm/OCM-wrong-nb-elements.txt";
228         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
229         try {
230             new ParserBuilder().
231             withMu(Constants.EIGEN5C_EARTH_MU).
232             buildOcmParser().
233             parseMessage(source);
234             Assertions.fail("an exception should have been thrown");
235         } catch (OrekitException oe) {
236             Assertions.assertEquals(OrekitMessages.WRONG_NB_COMPONENTS, oe.getSpecifier());
237             Assertions.assertEquals(OrbitElementsType.CARTP.toString(), oe.getParts()[0]);
238             Assertions.assertEquals(3, ((Integer) oe.getParts()[1]).intValue());
239             Assertions.assertEquals(6, ((Integer) oe.getParts()[2]).intValue());
240         }
241     }
242 
243     @Test
244     public void testUnknownFrame() {
245         final String name = "/ccsds/odm/ocm/OCM-unknown-frame.txt";
246         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
247         final Ocm    ocm    = new ParserBuilder().
248                                   withMu(Constants.EIGEN5C_EARTH_MU).
249                                   buildOcmParser().
250                                   parseMessage(source);
251         Assertions.assertEquals("CS GROUP", ocm.getHeader().getOriginator());
252         Assertions.assertEquals("728b0d2a-01fc-4d0e-9f0a-370c6930ea84", ocm.getHeader().getMessageId().toLowerCase(Locale.US));
253         final TrajectoryStateHistory h = ocm.getData().getTrajectoryBlocks().get(0);
254         Assertions.assertEquals("ZZRF", h.getMetadata().getTrajReferenceFrame().getName());
255         List<TimeStampedPVCoordinates> l = h.getCoordinates();
256         Assertions.assertEquals( 3.0e6, l.get(0).getPosition().getX(), 1.0e-9);
257         Assertions.assertEquals( 4.0e6, l.get(0).getPosition().getY(), 1.0e-9);
258         Assertions.assertEquals( 5.0e6, l.get(0).getPosition().getZ(), 1.0e-9);
259         Assertions.assertEquals(-1.0e3, l.get(0).getVelocity().getX(), 1.0e-12);
260         Assertions.assertEquals(-2.0e3, l.get(0).getVelocity().getY(), 1.0e-12);
261         Assertions.assertEquals(-3.0e3, l.get(0).getVelocity().getZ(), 1.0e-1);
262         try {
263             ocm.getData().getTrajectoryBlocks().get(0).getFrame();
264             Assertions.fail("an exception should have been thrown");
265         } catch (OrekitException oe) {
266             Assertions.assertEquals(OrekitMessages.CCSDS_INVALID_FRAME, oe.getSpecifier());
267             Assertions.assertEquals("ZZRF", oe.getParts()[0]);
268         }
269     }
270 
271     @Test
272     public void testUserDefined() {
273         final String name = "/ccsds/odm/ocm/OCM-user-defined.txt";
274         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
275         final Ocm    ocm    = new ParserBuilder().
276                                   withMu(Constants.EIGEN5C_EARTH_MU).
277                                   buildOcmParser().
278                                   parseMessage(source);
279         Assertions.assertEquals("CS GROUP", ocm.getHeader().getOriginator());
280         Assertions.assertEquals("b77d785c-f7a8-4a80-a9b1-a540eac19d7a", ocm.getHeader().getMessageId().toLowerCase(Locale.US));
281         Assertions.assertNull(ocm.getData().getTrajectoryBlocks());
282         Assertions.assertEquals(1, ocm.getData().getUserDefinedBlock().getComments().size());
283         Assertions.assertEquals("some user data", ocm.getData().getUserDefinedBlock().getComments().get(0));
284         Assertions.assertEquals(1, ocm.getData().getUserDefinedBlock().getParameters().size());
285         Assertions.assertEquals("OREKIT", ocm.getData().getUserDefinedBlock().getParameters().get("GENERATOR"));
286     }
287 
288     @Test
289     public void testParseOCM1() {
290         final String   name  = "/ccsds/odm/ocm/OCMExample1.txt";
291         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
292         final Ocm file = new ParserBuilder().
293                              withMu(Constants.EIGEN5C_EARTH_MU).
294                              buildOcmParser().
295                              parseMessage(source);
296 
297         // check the default values that are not set in this simple file
298         Assertions.assertEquals(1.0, file.getMetadata().getSclkSecPerSISec(), 1.0e-15);
299 
300         // Check Header Block;
301         Assertions.assertEquals(3.0, file.getHeader().getFormatVersion(), 1.0e-10);
302         Assertions.assertEquals(new AbsoluteDate(1998, 11, 6, 9, 23, 57, TimeScalesFactory.getUTC()),
303                             file.getHeader().getCreationDate());
304 
305         // OCM is the only message for which OBJECT_NAME is not mandatory, it is not present in this minimal file
306         Assertions.assertNull(file.getMetadata().getObjectName());
307 
308         Assertions.assertEquals("JAXA", file.getHeader().getOriginator());
309 
310         final AbsoluteDate t0 = new AbsoluteDate(1998, 12, 18, 14, 28, new TimeOffset(15, TimeOffset.SECOND, 117200, TimeOffset.MICROSECOND),
311                                                  TimeScalesFactory.getUTC());
312         Assertions.assertEquals(t0, file.getMetadata().getEpochT0());
313         Assertions.assertEquals(TimeSystem.UTC, file.getMetadata().getTimeSystem());
314 
315         // trajectory data
316         Assertions.assertEquals(1, file.getData().getTrajectoryBlocks().size());
317         TrajectoryStateHistory history = file.getData().getTrajectoryBlocks().get(0);
318         Assertions.assertEquals("intervening data records omitted between DT=20.0 and DT=500.0",
319                             history.getMetadata().getComments().get(0));
320         Assertions.assertEquals("OSCULATING", history.getMetadata().getOrbAveraging());
321         Assertions.assertEquals("EARTH", history.getMetadata().getCenter().getName());
322         Assertions.assertEquals(CelestialBodyFrame.ITRF2000, history.getMetadata().getTrajReferenceFrame().asCelestialBodyFrame());
323         Assertions.assertEquals(OrbitElementsType.CARTPV, history.getMetadata().getTrajType());
324         Assertions.assertEquals(0.0, file.getMetadata().getEpochT0().durationFrom(t0), 1.0e-15);
325         List<TrajectoryState> states = history.getTrajectoryStates();
326         Assertions.assertEquals(4, states.size());
327 
328         Assertions.assertEquals(0.0, states.get(0).getDate().durationFrom(t0), 1.0e-15);
329         Assertions.assertEquals(6, states.get(0).getElements().length);
330         Assertions.assertEquals( 2789600.0, states.get(0).getElements()[0], 1.0e-15);
331         Assertions.assertEquals( -280000.0, states.get(0).getElements()[1], 1.0e-15);
332         Assertions.assertEquals(-1746800.0, states.get(0).getElements()[2], 1.0e-15);
333         Assertions.assertEquals(    4730.0, states.get(0).getElements()[3], 1.0e-15);
334         Assertions.assertEquals(   -2500.0, states.get(0).getElements()[4], 1.0e-15);
335         Assertions.assertEquals(   -1040.0, states.get(0).getElements()[5], 1.0e-15);
336 
337         Assertions.assertEquals(10.0, states.get(1).getDate().durationFrom(t0), 1.0e-15);
338         Assertions.assertEquals(6, states.get(1).getElements().length);
339         Assertions.assertEquals( 2783400.0, states.get(1).getElements()[0], 1.0e-15);
340         Assertions.assertEquals( -308100.0, states.get(1).getElements()[1], 1.0e-15);
341         Assertions.assertEquals(-1877100.0, states.get(1).getElements()[2], 1.0e-15);
342         Assertions.assertEquals(    5190.0, states.get(1).getElements()[3], 1.0e-15);
343         Assertions.assertEquals(   -2420.0, states.get(1).getElements()[4], 1.0e-15);
344         Assertions.assertEquals(   -2000.0, states.get(1).getElements()[5], 1.0e-15);
345 
346         Assertions.assertEquals(20.0, states.get(2).getDate().durationFrom(t0), 1.0e-15);
347         Assertions.assertEquals(6, states.get(2).getElements().length);
348         Assertions.assertEquals( 2776000.0, states.get(2).getElements()[0], 1.0e-15);
349         Assertions.assertEquals( -336900.0, states.get(2).getElements()[1], 1.0e-15);
350         Assertions.assertEquals(-2008700.0, states.get(2).getElements()[2], 1.0e-15);
351         Assertions.assertEquals(    5640.0, states.get(2).getElements()[3], 1.0e-15);
352         Assertions.assertEquals(   -2340.0, states.get(2).getElements()[4], 1.0e-15);
353         Assertions.assertEquals(   -1950.0, states.get(2).getElements()[5], 1.0e-15);
354 
355         Assertions.assertEquals(500.0, states.get(3).getDate().durationFrom(t0), 1.0e-15);
356         Assertions.assertEquals(6, states.get(3).getElements().length);
357         Assertions.assertEquals( 2164375.0,  states.get(3).getElements()[0], 1.0e-15);
358         Assertions.assertEquals( 1115811.0,  states.get(3).getElements()[1], 1.0e-15);
359         Assertions.assertEquals( -688131.0,  states.get(3).getElements()[2], 1.0e-15);
360         Assertions.assertEquals(   -3533.28, states.get(3).getElements()[3], 1.0e-15);
361         Assertions.assertEquals(   -2884.52, states.get(3).getElements()[4], 1.0e-15);
362         Assertions.assertEquals(     885.35, states.get(3).getElements()[5], 1.0e-15);
363 
364         Assertions.assertEquals(1, file.getSatellites().size());
365         Assertions.assertEquals("UNKNOWN", file.getSatellites().entrySet().iterator().next().getKey());
366 
367     }
368 
369     @Test
370     public void testParseOCM2KVN() {
371         final String  name = "/ccsds/odm/ocm/OCMExample2.txt";
372         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
373         final Ocm file = new ParserBuilder().
374                              withMu(Constants.EIGEN5C_EARTH_MU).
375                              buildOcmParser().
376                              parseMessage(source);
377 
378         // Check Header Block;
379         Assertions.assertEquals(3.0, file.getHeader().getFormatVersion(), 1.0e-10);
380         Assertions.assertEquals("This OCM reflects the latest conditions post-maneuver A67Z",
381                             file.getHeader().getComments().get(0));
382         Assertions.assertEquals("This example shows the specification of multiple comment lines",
383                             file.getHeader().getComments().get(1));
384         Assertions.assertEquals(new AbsoluteDate(1998, 11, 6, 9, 23, 57, TimeScalesFactory.getUTC()),
385                             file.getHeader().getCreationDate());
386         Assertions.assertEquals("JAXA", file.getHeader().getOriginator());
387         Assertions.assertEquals("OCM 201113719185", file.getHeader().getMessageId());
388 
389         // Check metadata
390         Assertions.assertEquals("OSPREY 5",                                           file.getMetadata().getObjectName());
391         Assertions.assertEquals("1998-999A",                                          file.getMetadata().getInternationalDesignator());
392         Assertions.assertEquals("R. Rabbit",                                          file.getMetadata().getOriginatorPOC());
393         Assertions.assertEquals("Flight Dynamics Mission Design Lead",                file.getMetadata().getOriginatorPosition());
394         Assertions.assertEquals("(719)555-1234",                                      file.getMetadata().getOriginatorPhone());
395         Assertions.assertEquals("R.Rabbit@example.net",                               file.getMetadata().getOriginatorEmail());
396         Assertions.assertEquals("5040 Spaceflight Ave., Cocoa Beach, FL, USA, 12345", file.getMetadata().getOriginatorAddress());
397         Assertions.assertEquals("Mr. Rodgers",                                        file.getMetadata().getTechPOC());
398         Assertions.assertEquals("(719)555-4321",                                      file.getMetadata().getTechPhone());
399         Assertions.assertEquals("Rodgers@elsewhere.org",                              file.getMetadata().getTechEmail());
400         Assertions.assertEquals("125 CCSDS Road, Easter Island",                      file.getMetadata().getTechAddress());
401         Assertions.assertEquals(TimeSystem.UT1, file.getMetadata().getTimeSystem());
402         TimeScale ts = DataContext.getDefault().getTimeScales().getUT1(IERSConventions.IERS_2010, false);
403         Assertions.assertEquals(0.0,
404                             file.getMetadata().getEpochT0().durationFrom(new AbsoluteDate(1998, 12, 18, ts)),
405                             1.0e-10);
406         Assertions.assertEquals(36.0,                                  file.getMetadata().getTaimutcT0(),       1.0e-15);
407         Assertions.assertEquals(0.0,
408                             file.getMetadata().getNextLeapEpoch().
409                             durationFrom(new AbsoluteDate(2016, 12, 31, 23, 59, 60.0, TimeScalesFactory.getUTC())),
410                             3.0e-5);
411         Assertions.assertEquals(37.0,                                  file.getMetadata().getNextLeapTaimutc(), 1.0e-15);
412         Assertions.assertEquals(0.357,                                 file.getMetadata().getUt1mutcT0(),       1.0e-15);
413 
414         // check trajectory data
415         Assertions.assertEquals(1, file.getData().getTrajectoryBlocks().size());
416         final TrajectoryStateHistory orb = file.getData().getTrajectoryBlocks().get(0);
417         Assertions.assertEquals(2, orb.getMetadata().getComments().size());
418         Assertions.assertEquals("GEOCENTRIC, CARTESIAN, EARTH FIXED", orb.getMetadata().getComments().get(0));
419         Assertions.assertEquals("THIS IS MY SECOND COMMENT LINE",     orb.getMetadata().getComments().get(1));
420         Assertions.assertEquals("PREDICTED", orb.getMetadata().getTrajBasis());
421         Assertions.assertEquals("EFG", orb.getMetadata().getTrajReferenceFrame().getName());
422         Assertions.assertNull(orb.getMetadata().getTrajReferenceFrame().asFrame());
423         Assertions.assertNull(orb.getMetadata().getTrajReferenceFrame().asCelestialBodyFrame());
424         Assertions.assertNull(orb.getMetadata().getTrajReferenceFrame().asOrbitRelativeFrame());
425         Assertions.assertNull(orb.getMetadata().getTrajReferenceFrame().asSpacecraftBodyFrame());
426         Assertions.assertEquals(OrbitElementsType.CARTPV, orb.getMetadata().getTrajType());
427         Assertions.assertEquals(6, orb.getMetadata().getTrajUnits().size());
428         Assertions.assertEquals("km",   orb.getMetadata().getTrajUnits().get(0).getName());
429         Assertions.assertEquals("km",   orb.getMetadata().getTrajUnits().get(1).getName());
430         Assertions.assertEquals("km",   orb.getMetadata().getTrajUnits().get(2).getName());
431         Assertions.assertEquals("km/s", orb.getMetadata().getTrajUnits().get(3).getName());
432         Assertions.assertEquals("km/s", orb.getMetadata().getTrajUnits().get(4).getName());
433         Assertions.assertEquals("km/s", orb.getMetadata().getTrajUnits().get(5).getName());
434         Assertions.assertEquals(1, orb.getTrajectoryStates().size());
435         Assertions.assertEquals(new AbsoluteDate(1998, 12, 18, 14, 28, new TimeOffset(25L, 117200000000000000L), ts),
436                                 orb.getTrajectoryStates().get(0).getDate());
437         Assertions.assertEquals( 2854533.0, orb.getTrajectoryStates().get(0).getElements()[0], 1.0e-10);
438         Assertions.assertEquals(-2916187.0, orb.getTrajectoryStates().get(0).getElements()[1], 1.0e-10);
439         Assertions.assertEquals(-5360774.0, orb.getTrajectoryStates().get(0).getElements()[2], 1.0e-10);
440         Assertions.assertEquals(    5688.0, orb.getTrajectoryStates().get(0).getElements()[3], 1.0e-10);
441         Assertions.assertEquals(    4652.0, orb.getTrajectoryStates().get(0).getElements()[4], 1.0e-10);
442         Assertions.assertEquals(     520.0, orb.getTrajectoryStates().get(0).getElements()[5], 1.0e-10);
443 
444         // check physical data
445         OrbitPhysicalProperties phys = file.getData().getPhysicBlock();
446         Assertions.assertEquals(1, phys.getComments().size());
447         Assertions.assertEquals("S/C Physical Characteristics:", phys.getComments().get(0));
448         Assertions.assertEquals(100.0,   phys.getWetMass(),                  1.0e-10);
449         Assertions.assertEquals(0.03123, phys.getOebQ().getQ1(),             1.0e-10);
450         Assertions.assertEquals(0.78543, phys.getOebQ().getQ2(),             1.0e-10);
451         Assertions.assertEquals(0.39158, phys.getOebQ().getQ3(),             1.0e-10);
452         Assertions.assertEquals(0.47832, phys.getOebQ().getQ0(),             1.0e-10);
453         Assertions.assertEquals(2.0,     phys.getOebMax(),                   1.0e-10);
454         Assertions.assertEquals(1.0,     phys.getOebIntermediate(),          1.0e-10);
455         Assertions.assertEquals(0.5,     phys.getOebMin(),                   1.0e-10);
456         Assertions.assertEquals(0.15,    phys.getOebAreaAlongMax(),          1.0e-10);
457         Assertions.assertEquals(0.30,    phys.getOebAreaAlongIntermediate(), 1.0e-10);
458         Assertions.assertEquals(0.50,    phys.getOebAreaAlongMin(),          1.0e-10);
459 
460         // check no covariance
461         Assertions.assertNull(file.getData().getCovarianceBlocks());
462 
463         // check no maneuvers
464         Assertions.assertNull(file.getData().getManeuverBlocks());
465 
466         // check perturbation data
467         Perturbations perts = file.getData().getPerturbationsBlock();
468         Assertions.assertEquals(1, perts.getComments().size());
469         Assertions.assertEquals("Perturbations Specification:", perts.getComments().get(0));
470         Assertions.assertEquals("NRLMSIS00", perts.getAtmosphericModel());
471         Assertions.assertEquals("EGM-96", perts.getGravityModel());
472         Assertions.assertEquals(36, perts.getGravityDegree());
473         Assertions.assertEquals(36, perts.getGravityOrder());
474         Assertions.assertEquals(2, perts.getNBodyPerturbations().size());
475         Assertions.assertEquals("MOON", perts.getNBodyPerturbations().get(0).getName());
476         Assertions.assertEquals("SUN",  perts.getNBodyPerturbations().get(1).getName());
477         Assertions.assertEquals( 12.0, Units.NANO_TESLA.fromSI(perts.getFixedGeomagneticKp()), 1.0e-10);
478         Assertions.assertEquals(105.0, Unit.SOLAR_FLUX_UNIT.fromSI(perts.getFixedF10P7()),     1.0e-10);
479         Assertions.assertEquals(120.0, Unit.SOLAR_FLUX_UNIT.fromSI(perts.getFixedF10P7Mean()), 1.0e-10);
480 
481         // check no orbit determination
482         Assertions.assertNull(file.getData().getOrbitDeterminationBlock());
483 
484         // check user data
485         UserDefined user = file.getData().getUserDefinedBlock();
486         Assertions.assertTrue(user.getComments().isEmpty());
487         Assertions.assertEquals(1, user.getParameters().size());
488         Assertions.assertEquals("MAXWELL RAFERTY", user.getParameters().get("CONSOLE_POC"));
489 
490         Assertions.assertEquals(1, file.getSatellites().size());
491         Assertions.assertEquals("OSPREY 5", file.getSatellites().entrySet().iterator().next().getKey());
492 
493     }
494 
495     @Test
496     public void testParseOCM2XMLBinary() {
497         final String  name = "/ccsds/odm/ocm/OCMExample2.xml";
498         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
499         validateOCM2XML(new ParserBuilder().
500                         withMu(Constants.EIGEN5C_EARTH_MU).
501                         buildOcmParser().
502                         parseMessage(source));
503     }
504 
505     @Test
506     public void testParseOCM2XMLCharacter() {
507         final String  name = "/ccsds/odm/ocm/OCMExample2.xml";
508         final DataSource source = new DataSource(name, () -> new InputStreamReader(getClass().getResourceAsStream(name), StandardCharsets.UTF_8));
509         validateOCM2XML(new ParserBuilder().
510                         withMu(Constants.EIGEN5C_EARTH_MU).
511                         buildOcmParser().
512                         parseMessage(source));
513     }
514 
515     @Test
516     public void testWriteOCM2() throws IOException {
517         final String name = "/ccsds/odm/ocm/OCMExample2.xml";
518         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
519         OcmParser parser = new ParserBuilder(). withMu(Constants.EIGEN5C_EARTH_MU).buildOcmParser();
520         final Ocm original = parser.parseMessage(source);
521 
522         // write the parsed file back to a characters array
523         final CharArrayWriter caw = new CharArrayWriter();
524         final Generator generator = new KvnGenerator(caw, OcmWriter.KVN_PADDING_WIDTH, "dummy",
525                                                      Constants.JULIAN_DAY, 60);
526         new WriterBuilder().buildOcmWriter().writeMessage(generator, original);
527 
528         // reparse the written file
529         final byte[]     bytes   = caw.toString().getBytes(StandardCharsets.UTF_8);
530         final DataSource source2 = new DataSource(name, () -> new ByteArrayInputStream(bytes));
531         final Ocm    rebuilt = new ParserBuilder().buildOcmParser().parseMessage(source2);
532         validateOCM2XML(rebuilt);
533 
534     }
535 
536     private void validateOCM2XML(final Ocm file) {
537 
538         // Check Header Block;
539         Assertions.assertEquals(3.0, file.getHeader().getFormatVersion(), 1.0e-10);
540         Assertions.assertEquals("ODM V.3 Example G-2",
541                             file.getHeader().getComments().get(0));
542         Assertions.assertEquals("OCM example with space object characteristics and perturbations.",
543                             file.getHeader().getComments().get(1));
544         Assertions.assertEquals("This OCM reflects the latest conditions post-maneuver A67Z",
545                             file.getHeader().getComments().get(2));
546         Assertions.assertEquals("This example shows the specification of multiple comment lines",
547                             file.getHeader().getComments().get(3));
548         Assertions.assertEquals(new AbsoluteDate(1998, 11, 6, 9, 23, 57, TimeScalesFactory.getUTC()),
549                             file.getHeader().getCreationDate());
550         Assertions.assertEquals("JAXA", file.getHeader().getOriginator());
551         Assertions.assertEquals("OCM 201113719185", file.getHeader().getMessageId());
552 
553         // Check metadata
554         Assertions.assertNull(file.getMetadata().getObjectName());
555         Assertions.assertNull(file.getMetadata().getObjectDesignator());
556         Assertions.assertEquals("1998-999A",                           file.getMetadata().getInternationalDesignator());
557         Assertions.assertEquals("R. Rabbit",                           file.getMetadata().getOriginatorPOC());
558         Assertions.assertEquals("Flight Dynamics Mission Design Lead", file.getMetadata().getOriginatorPosition());
559         Assertions.assertEquals("(719)555-1234",                       file.getMetadata().getOriginatorPhone());
560         Assertions.assertEquals("Mr. Rodgers",                         file.getMetadata().getTechPOC());
561         Assertions.assertEquals("(719)555-1234",                       file.getMetadata().getTechPhone());
562         Assertions.assertEquals("email@email.XXX",                     file.getMetadata().getTechAddress());
563         Assertions.assertEquals(TimeSystem.UT1, file.getMetadata().getTimeSystem());
564         TimeScale ts = DataContext.getDefault().getTimeScales().getUT1(IERSConventions.IERS_2010, false);
565         Assertions.assertEquals(0.0,
566                             file.getMetadata().getEpochT0().durationFrom(new AbsoluteDate(1998, 12, 18, ts)),
567                             1.0e-10);
568         Assertions.assertEquals(36.0,                                  file.getMetadata().getTaimutcT0(), 1.0e-15);
569         Assertions.assertEquals(0.357,                                 file.getMetadata().getUt1mutcT0(), 1.0e-15);
570 
571         // check trajectory data
572         Assertions.assertEquals(1, file.getData().getTrajectoryBlocks().size());
573         final TrajectoryStateHistory orb = file.getData().getTrajectoryBlocks().get(0);
574         Assertions.assertEquals(2, orb.getMetadata().getComments().size());
575         Assertions.assertEquals("GEOCENTRIC, CARTESIAN, EARTH FIXED", orb.getMetadata().getComments().get(0));
576         Assertions.assertEquals("THIS IS MY SECOND COMMENT LINE",     orb.getMetadata().getComments().get(1));
577         Assertions.assertEquals("PREDICTED", orb.getMetadata().getTrajBasis());
578         Assertions.assertEquals("EFG", orb.getMetadata().getTrajReferenceFrame().getName());
579         Assertions.assertNull(orb.getMetadata().getTrajReferenceFrame().asFrame());
580         Assertions.assertNull(orb.getMetadata().getTrajReferenceFrame().asCelestialBodyFrame());
581         Assertions.assertNull(orb.getMetadata().getTrajReferenceFrame().asOrbitRelativeFrame());
582         Assertions.assertNull(orb.getMetadata().getTrajReferenceFrame().asSpacecraftBodyFrame());
583         Assertions.assertEquals(OrbitElementsType.CARTPVA, orb.getMetadata().getTrajType());
584         Assertions.assertNull(orb.getMetadata().getTrajUnits());
585         Assertions.assertEquals(1, orb.getTrajectoryStates().size());
586         Assertions.assertEquals(new AbsoluteDate(1998, 12, 18, 0, 0, 0.0, ts),
587                             orb.getTrajectoryStates().get(0).getDate());
588         Assertions.assertEquals( 2854500.0, orb.getTrajectoryStates().get(0).getElements()[0], 1.0e-10);
589         Assertions.assertEquals(-2916200.0, orb.getTrajectoryStates().get(0).getElements()[1], 1.0e-10);
590         Assertions.assertEquals(-5360700.0, orb.getTrajectoryStates().get(0).getElements()[2], 1.0e-10);
591         Assertions.assertEquals(    5900.0, orb.getTrajectoryStates().get(0).getElements()[3], 1.0e-10);
592         Assertions.assertEquals(    4860.0, orb.getTrajectoryStates().get(0).getElements()[4], 1.0e-10);
593         Assertions.assertEquals(     520.0, orb.getTrajectoryStates().get(0).getElements()[5], 1.0e-10);
594         Assertions.assertEquals(       3.7, orb.getTrajectoryStates().get(0).getElements()[6], 1.0e-10);
595         Assertions.assertEquals(      -3.8, orb.getTrajectoryStates().get(0).getElements()[7], 1.0e-10);
596         Assertions.assertEquals(      -7.0, orb.getTrajectoryStates().get(0).getElements()[8], 1.0e-10);
597 
598         // check physical data
599         OrbitPhysicalProperties phys = file.getData().getPhysicBlock();
600         Assertions.assertEquals(1, phys.getComments().size());
601         Assertions.assertEquals("Spacecraft Physical Characteristics", phys.getComments().get(0));
602         Assertions.assertEquals(100.0,   phys.getWetMass(),                  1.0e-10);
603         Assertions.assertEquals(0.03123, phys.getOebQ().getQ1(),             1.0e-10);
604         Assertions.assertEquals(0.78543, phys.getOebQ().getQ2(),             1.0e-10);
605         Assertions.assertEquals(0.39158, phys.getOebQ().getQ3(),             1.0e-10);
606         Assertions.assertEquals(0.47832, phys.getOebQ().getQ0(),             1.0e-10);
607         Assertions.assertEquals(2.0,     phys.getOebMax(),                   1.0e-10);
608         Assertions.assertEquals(1.0,     phys.getOebIntermediate(),          1.0e-10);
609         Assertions.assertEquals(0.5,     phys.getOebMin(),                   1.0e-10);
610         Assertions.assertEquals(0.15,    phys.getOebAreaAlongMax(),          1.0e-10);
611         Assertions.assertEquals(0.30,    phys.getOebAreaAlongIntermediate(), 1.0e-10);
612         Assertions.assertEquals(0.50,    phys.getOebAreaAlongMin(),          1.0e-10);
613 
614         // check no covariance
615         Assertions.assertNull(file.getData().getCovarianceBlocks());
616 
617         // check no maneuvers
618         Assertions.assertNull(file.getData().getManeuverBlocks());
619 
620         // check perturbation data
621         Perturbations perts = file.getData().getPerturbationsBlock();
622         Assertions.assertEquals(1, perts.getComments().size());
623         Assertions.assertEquals("Perturbations Specification", perts.getComments().get(0));
624         Assertions.assertEquals("NRLMSIS00", perts.getAtmosphericModel());
625         Assertions.assertEquals("EGM-96", perts.getGravityModel());
626         Assertions.assertEquals(36, perts.getGravityDegree());
627         Assertions.assertEquals(36, perts.getGravityOrder());
628         Assertions.assertEquals(36, perts.getGravityOrder());
629         Assertions.assertEquals(3.986004415e14, perts.getGm(), 1.0);
630         Assertions.assertEquals("MOON", perts.getNBodyPerturbations().get(0).getName());
631         Assertions.assertEquals("SUN",  perts.getNBodyPerturbations().get(1).getName());
632         Assertions.assertEquals( 12.0, Units.NANO_TESLA.fromSI(perts.getFixedGeomagneticKp()), 1.0e-10);
633         Assertions.assertEquals(105.0, Unit.SOLAR_FLUX_UNIT.fromSI(perts.getFixedF10P7()),     1.0e-10);
634         Assertions.assertEquals(120.0, Unit.SOLAR_FLUX_UNIT.fromSI(perts.getFixedF10P7Mean()), 1.0e-10);
635 
636         // check no orbit determination
637         Assertions.assertNull(file.getData().getOrbitDeterminationBlock());
638 
639         // check user data
640         UserDefined user = file.getData().getUserDefinedBlock();
641         Assertions.assertTrue(user.getComments().isEmpty());
642         Assertions.assertEquals(1, user.getParameters().size());
643         Assertions.assertEquals("WGS-84", user.getParameters().get("EARTH_MODEL"));
644 
645         Assertions.assertEquals(1, file.getSatellites().size());
646         Assertions.assertEquals("1998-999A", file.getSatellites().entrySet().iterator().next().getKey());
647     }
648 
649     @Test
650     public void testParseOCM3() {
651         final String   name  = "/ccsds/odm/ocm/OCMExample3.txt";
652         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
653         final Ocm file = new ParserBuilder().
654                              withMu(Constants.EIGEN5C_EARTH_MU).
655                              buildOcmParser().
656                              parse(source); // using EphemerisFileParser API here
657 
658         // Check Header Block;
659         TimeScale utc = TimeScalesFactory.getUTC();
660         Assertions.assertEquals(3.0, file.getHeader().getFormatVersion(), 1.0e-10);
661         Assertions.assertEquals(0, file.getMetadata().getComments().size());
662         Assertions.assertEquals(new AbsoluteDate(1998, 11, 6, 9, 23, 57, utc),
663                             file.getHeader().getCreationDate());
664 
665         final AbsoluteDate t0 = new AbsoluteDate(1998, 12, 18, 14, 28, new TimeOffset(15, TimeOffset.SECOND, 117200, TimeOffset.MICROSECOND), utc);
666         Assertions.assertEquals(t0, file.getMetadata().getEpochT0());
667         Assertions.assertEquals("UTC", file.getMetadata().getTimeSystem().name());
668 
669         // check trajectory data
670         Assertions.assertEquals(1, file.getData().getTrajectoryBlocks().size());
671         final TrajectoryStateHistory orb = file.getData().getTrajectoryBlocks().get(0);
672         Assertions.assertEquals(2, orb.getMetadata().getComments().size());
673         Assertions.assertEquals("ORBIT EPHEMERIS INCORPORATING DEPLOYMENTS AND MANEUVERS (BELOW)", orb.getMetadata().getComments().get(0));
674         Assertions.assertEquals("intervening data records omitted after DT=20.0",     orb.getMetadata().getComments().get(1));
675         // TRAJ_BASIS has no default in 502.0-B-3 (p. 6-16, Table 6-4)
676         Assertions.assertNull(orb.getMetadata().getTrajBasis()); // not present in the file
677         Assertions.assertEquals("TOD", orb.getMetadata().getTrajReferenceFrame().getName());
678         Assertions.assertNotNull(orb.getMetadata().getTrajReferenceFrame().asFrame());
679         Assertions.assertEquals(CelestialBodyFrame.TOD, orb.getMetadata().getTrajReferenceFrame().asCelestialBodyFrame());
680         Assertions.assertNull(orb.getMetadata().getTrajReferenceFrame().asOrbitRelativeFrame());
681         Assertions.assertNull(orb.getMetadata().getTrajReferenceFrame().asSpacecraftBodyFrame());
682         Assertions.assertEquals(t0, orb.getMetadata().getTrajFrameEpoch());
683         Assertions.assertEquals(OrbitElementsType.CARTPVA, orb.getMetadata().getTrajType());
684         Assertions.assertEquals(9, orb.getMetadata().getTrajUnits().size());
685         Assertions.assertEquals("km",      orb.getMetadata().getTrajUnits().get(0).getName());
686         Assertions.assertEquals("km",      orb.getMetadata().getTrajUnits().get(1).getName());
687         Assertions.assertEquals("km",      orb.getMetadata().getTrajUnits().get(2).getName());
688         Assertions.assertEquals("km/s",    orb.getMetadata().getTrajUnits().get(3).getName());
689         Assertions.assertEquals("km/s",    orb.getMetadata().getTrajUnits().get(4).getName());
690         Assertions.assertEquals("km/s",    orb.getMetadata().getTrajUnits().get(5).getName());
691         Assertions.assertEquals("km/s**2", orb.getMetadata().getTrajUnits().get(6).getName());
692         Assertions.assertEquals("km/s**2", orb.getMetadata().getTrajUnits().get(7).getName());
693         Assertions.assertEquals("km/s**2", orb.getMetadata().getTrajUnits().get(8).getName());
694         Assertions.assertEquals(4, orb.getTrajectoryStates().size());
695         Assertions.assertEquals(       0.0,  orb.getTrajectoryStates().get(0).getDate().durationFrom(t0), 1.0e-10);
696         Assertions.assertEquals( 2789600.0,  orb.getTrajectoryStates().get(0).getElements()[0], 1.0e-10);
697         Assertions.assertEquals( -280000.0,  orb.getTrajectoryStates().get(0).getElements()[1], 1.0e-10);
698         Assertions.assertEquals(-1746800.0,  orb.getTrajectoryStates().get(0).getElements()[2], 1.0e-10);
699         Assertions.assertEquals(    4730.0,  orb.getTrajectoryStates().get(0).getElements()[3], 1.0e-10);
700         Assertions.assertEquals(   -2500.0,  orb.getTrajectoryStates().get(0).getElements()[4], 1.0e-10);
701         Assertions.assertEquals(   -1040.0,  orb.getTrajectoryStates().get(0).getElements()[5], 1.0e-10);
702         Assertions.assertEquals(       8.0,  orb.getTrajectoryStates().get(0).getElements()[6], 1.0e-10);
703         Assertions.assertEquals(       1.0,  orb.getTrajectoryStates().get(0).getElements()[7], 1.0e-10);
704         Assertions.assertEquals(    -159.0,  orb.getTrajectoryStates().get(0).getElements()[8], 1.0e-10);
705         Assertions.assertEquals(      10.0,  orb.getTrajectoryStates().get(1).getDate().durationFrom(t0), 1.0e-10);
706         Assertions.assertEquals( 2783400.0,  orb.getTrajectoryStates().get(1).getElements()[0], 1.0e-10);
707         Assertions.assertEquals( -308100.0,  orb.getTrajectoryStates().get(1).getElements()[1], 1.0e-10);
708         Assertions.assertEquals(-1877100.0,  orb.getTrajectoryStates().get(1).getElements()[2], 1.0e-10);
709         Assertions.assertEquals(    5190.0,  orb.getTrajectoryStates().get(1).getElements()[3], 1.0e-10);
710         Assertions.assertEquals(   -2420.0,  orb.getTrajectoryStates().get(1).getElements()[4], 1.0e-10);
711         Assertions.assertEquals(   -2000.0,  orb.getTrajectoryStates().get(1).getElements()[5], 1.0e-10);
712         Assertions.assertEquals(       8.0,  orb.getTrajectoryStates().get(1).getElements()[6], 1.0e-10);
713         Assertions.assertEquals(       1.0,  orb.getTrajectoryStates().get(1).getElements()[7], 1.0e-10);
714         Assertions.assertEquals(       1.0,  orb.getTrajectoryStates().get(1).getElements()[8], 1.0e-10);
715         Assertions.assertEquals(      20.0,  orb.getTrajectoryStates().get(2).getDate().durationFrom(t0), 1.0e-10);
716         Assertions.assertEquals( 2776000.0,  orb.getTrajectoryStates().get(2).getElements()[0], 1.0e-10);
717         Assertions.assertEquals( -336900.0,  orb.getTrajectoryStates().get(2).getElements()[1], 1.0e-10);
718         Assertions.assertEquals(-2008700.0,  orb.getTrajectoryStates().get(2).getElements()[2], 1.0e-10);
719         Assertions.assertEquals(    5640.0,  orb.getTrajectoryStates().get(2).getElements()[3], 1.0e-10);
720         Assertions.assertEquals(   -2340.0,  orb.getTrajectoryStates().get(2).getElements()[4], 1.0e-10);
721         Assertions.assertEquals(   -1950.0,  orb.getTrajectoryStates().get(2).getElements()[5], 1.0e-10);
722         Assertions.assertEquals(       8.0,  orb.getTrajectoryStates().get(2).getElements()[6], 1.0e-10);
723         Assertions.assertEquals(       1.0,  orb.getTrajectoryStates().get(2).getElements()[7], 1.0e-10);
724         Assertions.assertEquals(     159.0,  orb.getTrajectoryStates().get(2).getElements()[8], 1.0e-10);
725         Assertions.assertEquals(     500.0,  orb.getTrajectoryStates().get(3).getDate().durationFrom(t0), 1.0e-10);
726         Assertions.assertEquals( 2164375.0,  orb.getTrajectoryStates().get(3).getElements()[0], 1.0e-10);
727         Assertions.assertEquals( 1115811.0,  orb.getTrajectoryStates().get(3).getElements()[1], 1.0e-10);
728         Assertions.assertEquals( -688131.0,  orb.getTrajectoryStates().get(3).getElements()[2], 1.0e-10);
729         Assertions.assertEquals(   -3533.28, orb.getTrajectoryStates().get(3).getElements()[3], 1.0e-10);
730         Assertions.assertEquals(   -2884.52, orb.getTrajectoryStates().get(3).getElements()[4], 1.0e-10);
731         Assertions.assertEquals(     885.35, orb.getTrajectoryStates().get(3).getElements()[5], 1.0e-10);
732         Assertions.assertEquals(       0.0,  orb.getTrajectoryStates().get(3).getElements()[6], 1.0e-10);
733         Assertions.assertEquals(       0.0,  orb.getTrajectoryStates().get(3).getElements()[7], 1.0e-10);
734         Assertions.assertEquals(       0.0,  orb.getTrajectoryStates().get(3).getElements()[8], 1.0e-10);
735 
736         // check physical data
737         OrbitPhysicalProperties phys = file.getData().getPhysicBlock();
738         Assertions.assertEquals(1, phys.getComments().size());
739         Assertions.assertEquals("S/C Physical Characteristics:", phys.getComments().get(0));
740         Assertions.assertEquals(10.0,    phys.getDragConstantArea(),         1.0e-10);
741         Assertions.assertEquals(2.3,     phys.getDragCoefficient(),   1.0e-10);
742         Assertions.assertEquals(100.0,   phys.getWetMass(),                  1.0e-10);
743         Assertions.assertEquals(4.0,     phys.getSrpConstantArea(),          1.0e-10);
744         Assertions.assertEquals(1.3,     phys.getSrpCoefficient(),    1.0e-10);
745 
746         // check no covariance
747         Assertions.assertNull(file.getData().getCovarianceBlocks());
748 
749         // check maneuvers
750         List<OrbitManeuverHistory> man = file.getData().getManeuverBlocks();
751         Assertions.assertEquals(2, man.size());
752 
753         Assertions.assertEquals(2, man.get(0).getMetadata().getComments().size());
754         Assertions.assertEquals("Ten 1kg objects deployed from 200kg host over 100 s timespan", man.get(0).getMetadata().getComments().get(0));
755         Assertions.assertEquals("20 deg off of back-track direction", man.get(0).getMetadata().getComments().get(1));
756         Assertions.assertEquals("CUBESAT_DEPLOY", man.get(0).getMetadata().getManID());
757         Assertions.assertEquals(ManBasis.CANDIDATE, man.get(0).getMetadata().getManBasis());
758         Assertions.assertEquals("DEPLOY",           man.get(0).getMetadata().getManDeviceID());
759         Assertions.assertEquals(1,                  man.get(0).getMetadata().getManPurpose().size());
760         Assertions.assertEquals("DEPLOY",           man.get(0).getMetadata().getManPurpose().get(0));
761         Assertions.assertEquals("RSW_ROTATING",     man.get(0).getMetadata().getManReferenceFrame().getName());
762         Assertions.assertNull(man.get(0).getMetadata().getManReferenceFrame().asFrame());
763         Assertions.assertNull(man.get(0).getMetadata().getManReferenceFrame().asCelestialBodyFrame());
764         Assertions.assertEquals(OrbitRelativeFrame.RSW_ROTATING, man.get(0).getMetadata().getManReferenceFrame().asOrbitRelativeFrame());
765         Assertions.assertNull(man.get(0).getMetadata().getManReferenceFrame().asSpacecraftBodyFrame());
766         Assertions.assertEquals(9, man.get(0).getMetadata().getManComposition().size());
767         Assertions.assertEquals(ManeuverFieldType.TIME_RELATIVE,   man.get(0).getMetadata().getManComposition().get(0));
768         Assertions.assertEquals(ManeuverFieldType.DEPLOY_ID,       man.get(0).getMetadata().getManComposition().get(1));
769         Assertions.assertEquals(ManeuverFieldType.DEPLOY_DV_X,     man.get(0).getMetadata().getManComposition().get(2));
770         Assertions.assertEquals(ManeuverFieldType.DEPLOY_DV_Y,     man.get(0).getMetadata().getManComposition().get(3));
771         Assertions.assertEquals(ManeuverFieldType.DEPLOY_DV_Z,     man.get(0).getMetadata().getManComposition().get(4));
772         Assertions.assertEquals(ManeuverFieldType.DEPLOY_MASS,     man.get(0).getMetadata().getManComposition().get(5));
773         Assertions.assertEquals(ManeuverFieldType.DEPLOY_DV_SIGMA, man.get(0).getMetadata().getManComposition().get(6));
774         Assertions.assertEquals(ManeuverFieldType.DEPLOY_DV_RATIO, man.get(0).getMetadata().getManComposition().get(7));
775         Assertions.assertEquals(ManeuverFieldType.DEPLOY_DV_CDA,   man.get(0).getMetadata().getManComposition().get(8));
776         Assertions.assertEquals(8, man.get(0).getMetadata().getManUnits().size());
777         Assertions.assertEquals("n/a",  man.get(0).getMetadata().getManUnits().get(0).getName());
778         Assertions.assertEquals("km/s", man.get(0).getMetadata().getManUnits().get(1).getName());
779         Assertions.assertEquals("km/s", man.get(0).getMetadata().getManUnits().get(2).getName());
780         Assertions.assertEquals("km/s", man.get(0).getMetadata().getManUnits().get(3).getName());
781         Assertions.assertEquals("kg",   man.get(0).getMetadata().getManUnits().get(4).getName());
782         Assertions.assertEquals("%",    man.get(0).getMetadata().getManUnits().get(5).getName());
783         Assertions.assertEquals("n/a",  man.get(0).getMetadata().getManUnits().get(6).getName());
784         Assertions.assertEquals("m**2", man.get(0).getMetadata().getManUnits().get(7).getName());
785         Assertions.assertEquals(10, man.get(0).getManeuvers().size());
786         Assertions.assertEquals(500.0,        man.get(0).getManeuvers().get(0).getDate().durationFrom(t0), 1.0e-10);
787         Assertions.assertEquals("CUBESAT_10", man.get(0).getManeuvers().get(0).getDeployId());
788         Assertions.assertEquals( 2.8773E-1,   man.get(0).getManeuvers().get(0).getDeployDv().getX(), 1.0e-10);
789         Assertions.assertEquals(-9.3969E-1,   man.get(0).getManeuvers().get(0).getDeployDv().getY(), 1.0e-10);
790         Assertions.assertEquals( 1.8491E-1,   man.get(0).getManeuvers().get(0).getDeployDv().getZ(), 1.0e-10);
791         Assertions.assertEquals(-1.0,         man.get(0).getManeuvers().get(0).getDeployMass(), 1.0e-10);
792         Assertions.assertEquals( 0.05,        man.get(0).getManeuvers().get(0).getDeployDvSigma(), 1.0e-10);
793         Assertions.assertEquals(-0.005025,    man.get(0).getManeuvers().get(0).getDeployDvRatio(), 1.0e-10);
794         Assertions.assertEquals( 0.033,       man.get(0).getManeuvers().get(0).getDeployDvCda(), 1.0e-10);
795         Assertions.assertEquals(510.0,        man.get(0).getManeuvers().get(1).getDate().durationFrom(t0), 1.0e-10);
796         Assertions.assertEquals("CUBESAT_11", man.get(0).getManeuvers().get(1).getDeployId());
797         Assertions.assertEquals( 1.4208E-1,   man.get(0).getManeuvers().get(1).getDeployDv().getX(), 1.0e-10);
798         Assertions.assertEquals(-9.3969E-1,   man.get(0).getManeuvers().get(1).getDeployDv().getY(), 1.0e-10);
799         Assertions.assertEquals( 3.1111E-1,   man.get(0).getManeuvers().get(1).getDeployDv().getZ(), 1.0e-10);
800         Assertions.assertEquals(-1.0,         man.get(0).getManeuvers().get(1).getDeployMass(), 1.0e-10);
801         Assertions.assertEquals( 0.05,        man.get(0).getManeuvers().get(1).getDeployDvSigma(), 1.0e-10);
802         Assertions.assertEquals(-0.005051,    man.get(0).getManeuvers().get(1).getDeployDvRatio(), 1.0e-10);
803         Assertions.assertEquals( 0.033,       man.get(0).getManeuvers().get(1).getDeployDvCda(), 1.0e-10);
804 
805         Assertions.assertEquals(1, man.get(1).getMetadata().getComments().size());
806         Assertions.assertEquals("100 s of 0.5N +in-track thrust w/effic η=0.95, Isp=300s, 5% 1-sigma error", man.get(1).getMetadata().getComments().get(0));
807         Assertions.assertEquals("E_W_20160305B",    man.get(1).getMetadata().getManID());
808         Assertions.assertEquals(ManBasis.CANDIDATE, man.get(1).getMetadata().getManBasis());
809         Assertions.assertEquals("THR_01",           man.get(1).getMetadata().getManDeviceID());
810         Assertions.assertEquals(1,                  man.get(1).getMetadata().getManPurpose().size());
811         Assertions.assertEquals("ORBIT",            man.get(1).getMetadata().getManPurpose().get(0));
812         Assertions.assertEquals("RSW_ROTATING",     man.get(1).getMetadata().getManReferenceFrame().getName());
813         Assertions.assertNull(man.get(1).getMetadata().getManReferenceFrame().asFrame());
814         Assertions.assertNull(man.get(1).getMetadata().getManReferenceFrame().asCelestialBodyFrame());
815         Assertions.assertEquals(OrbitRelativeFrame.RSW_ROTATING, man.get(1).getMetadata().getManReferenceFrame().asOrbitRelativeFrame());
816         Assertions.assertEquals(11,                              man.get(1).getMetadata().getManComposition().size());
817         Assertions.assertEquals(ManeuverFieldType.TIME_ABSOLUTE, man.get(1).getMetadata().getManComposition().get( 0));
818         Assertions.assertEquals(ManeuverFieldType.TIME_RELATIVE, man.get(1).getMetadata().getManComposition().get( 1));
819         Assertions.assertEquals(ManeuverFieldType.MAN_DURA,      man.get(1).getMetadata().getManComposition().get( 2));
820         Assertions.assertEquals(ManeuverFieldType.THR_X,         man.get(1).getMetadata().getManComposition().get( 3));
821         Assertions.assertEquals(ManeuverFieldType.THR_Y,         man.get(1).getMetadata().getManComposition().get( 4));
822         Assertions.assertEquals(ManeuverFieldType.THR_Z,         man.get(1).getMetadata().getManComposition().get( 5));
823         Assertions.assertEquals(ManeuverFieldType.THR_MAG_SIGMA, man.get(1).getMetadata().getManComposition().get( 6));
824         Assertions.assertEquals(ManeuverFieldType.THR_DIR_SIGMA, man.get(1).getMetadata().getManComposition().get( 7));
825         Assertions.assertEquals(ManeuverFieldType.THR_INTERP,    man.get(1).getMetadata().getManComposition().get( 8));
826         Assertions.assertEquals(ManeuverFieldType.THR_ISP,       man.get(1).getMetadata().getManComposition().get( 9));
827         Assertions.assertEquals(ManeuverFieldType.THR_EFFIC,     man.get(1).getMetadata().getManComposition().get(10));
828         Assertions.assertEquals(9, man.get(1).getMetadata().getManUnits().size());
829         Assertions.assertEquals("s",    man.get(1).getMetadata().getManUnits().get(0).getName());
830         Assertions.assertEquals("N",    man.get(1).getMetadata().getManUnits().get(1).getName());
831         Assertions.assertEquals("N",    man.get(1).getMetadata().getManUnits().get(2).getName());
832         Assertions.assertEquals("N",    man.get(1).getMetadata().getManUnits().get(3).getName());
833         Assertions.assertEquals("%",    man.get(1).getMetadata().getManUnits().get(4).getName());
834         Assertions.assertEquals("deg",  man.get(1).getMetadata().getManUnits().get(5).getName());
835         Assertions.assertEquals("n/a",  man.get(1).getMetadata().getManUnits().get(6).getName());
836         Assertions.assertEquals("s",    man.get(1).getMetadata().getManUnits().get(7).getName());
837         Assertions.assertEquals("n/a",  man.get(1).getMetadata().getManUnits().get(8).getName());
838         Assertions.assertEquals(1,      man.get(1).getManeuvers().size());
839         Assertions.assertEquals(500.0,        man.get(1).getManeuvers().get(0).getDate().durationFrom(t0), 1.0e-10);
840         Assertions.assertEquals(100,          man.get(1).getManeuvers().get(0).getDuration(),         1.0e-10);
841         Assertions.assertEquals(  0.0,        man.get(1).getManeuvers().get(0).getThrust().getX(),    1.0e-10);
842         Assertions.assertEquals(  0.5,        man.get(1).getManeuvers().get(0).getThrust().getY(),    1.0e-10);
843         Assertions.assertEquals( 0.05,        man.get(1).getManeuvers().get(0).getThrustMagnitudeSigma(), 1.0e-10);
844         Assertions.assertEquals( 1.0,         FastMath.toDegrees(man.get(1).getManeuvers().get(0).getThrustDirectionSigma()), 1.0e-10);
845         Assertions.assertEquals(OnOff.ON,     man.get(1).getManeuvers().get(0).getThrustInterpolation());
846         Assertions.assertTrue(man.get(1).getManeuvers().get(0).getThrustInterpolation().isOn());
847         Assertions.assertEquals(300.0,        man.get(1).getManeuvers().get(0).getThrustIsp(),        1.0e-10);
848         Assertions.assertEquals(0.95,         man.get(1).getManeuvers().get(0).getThrustEfficiency(), 1.0e-10);
849 
850         // check perturbation data
851         Perturbations perts = file.getData().getPerturbationsBlock();
852         Assertions.assertEquals(1, perts.getComments().size());
853         Assertions.assertEquals("Perturbations specification", perts.getComments().get(0));
854         Assertions.assertEquals(3.986004415e14, perts.getGm(),     1.0);
855 
856         // check orbit determination
857         OrbitDetermination od = file.getData().getOrbitDeterminationBlock();
858         Assertions.assertEquals(1, od.getComments().size());
859         Assertions.assertEquals("Orbit Determination information", od.getComments().get(0));
860         Assertions.assertEquals("OOD #10059",    od.getId());
861         Assertions.assertEquals("OOD #10058",    od.getPrevId());
862         Assertions.assertEquals(OdMethodType.SF, od.getMethod().getType());
863         Assertions.assertEquals("ODTK",          od.getMethod().getTool());
864         Assertions.assertEquals(new AbsoluteDate(2001, 11, 6, 11, 17, 33.0, utc), od.getEpoch());
865         Assertions.assertEquals(273, od.getObsUsed());
866         Assertions.assertEquals( 91, od.getTracksUsed());
867 
868         // check no user data
869         Assertions.assertNull(file.getData().getUserDefinedBlock());
870 
871     }
872 
873     @Test
874     public void testParseOCM4() {
875         final String   name  = "/ccsds/odm/ocm/OCMExample4.txt";
876         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
877         final Ocm file = new ParserBuilder().
878                              withMu(Constants.IERS2003_EARTH_MU).
879                              buildOcmParser().
880                              parseMessage(source);
881 
882         // Check Header Block;
883         Assertions.assertEquals(3.0, file.getHeader().getFormatVersion(), 1.0e-10);
884         Assertions.assertEquals(2, file.getHeader().getComments().size());
885         Assertions.assertEquals("This file is a dummy example with inconsistent data", file.getHeader().getComments().get(0));
886         Assertions.assertEquals("it is used to exercise all possible keys in Key-Value Notation", file.getHeader().getComments().get(1));
887         Assertions.assertEquals("dummy-classification",                file.getHeader().getClassification());
888         Assertions.assertEquals(new AbsoluteDate(2019, 7, 23, 10, 29, new TimeOffset(31L, 576000000000000000L), TimeScalesFactory.getUTC()),
889                                 file.getHeader().getCreationDate());
890         Assertions.assertEquals("JPL",                                 file.getHeader().getOriginator());
891         Assertions.assertEquals("ABC-12_34",                           file.getHeader().getMessageId());
892 
893         // Check metadata
894         Assertions.assertEquals(1,                                                    file.getMetadata().getComments().size());
895         Assertions.assertEquals("Metadata comment",                                   file.getMetadata().getComments().get(0));
896         Assertions.assertEquals("POLYSAT",                                            file.getMetadata().getObjectName());
897         Assertions.assertEquals(3,                                                    file.getMetadata().getAlternateNames().size());
898         Assertions.assertEquals("ALTERNATE",                                          file.getMetadata().getAlternateNames().get(0));
899         Assertions.assertEquals("OTHER",                                              file.getMetadata().getAlternateNames().get(1));
900         Assertions.assertEquals("RELATED",                                            file.getMetadata().getAlternateNames().get(2));
901         Assertions.assertEquals("18SPCS 18571",                                       file.getMetadata().getObjectDesignator());
902         Assertions.assertEquals("2000-053A",                                          file.getMetadata().getInternationalDesignator());
903         Assertions.assertEquals("Mr. Rodgers",                                        file.getMetadata().getOriginatorPOC());
904         Assertions.assertEquals("Flight Dynamics Mission Design Lead",                file.getMetadata().getOriginatorPosition());
905         Assertions.assertEquals("+12345678901",                                       file.getMetadata().getOriginatorPhone());
906         Assertions.assertEquals("JOHN.DOE@EXAMPLE.ORG",                               file.getMetadata().getOriginatorEmail());
907         Assertions.assertEquals("5040 Spaceflight Ave., Cocoa Beach, FL, USA, 12345", file.getMetadata().getOriginatorAddress());
908         Assertions.assertEquals("NASA",                                                file.getMetadata().getTechOrg());
909         Assertions.assertEquals("Maxwell Smart",                                       file.getMetadata().getTechPOC());
910         Assertions.assertEquals("+98765432109",                                        file.getMetadata().getTechPhone());
911         Assertions.assertEquals("MAX@EXAMPLE.ORG",                                     file.getMetadata().getTechEmail());
912         Assertions.assertEquals("34 Orekit avenue, Earth",                             file.getMetadata().getTechAddress());
913         Assertions.assertEquals("ABC-12_33",                                           file.getMetadata().getPreviousMessageID());
914         Assertions.assertEquals("ABC-12_35",                                           file.getMetadata().getNextMessageID());
915         Assertions.assertEquals("ADM_MSG_35132.TXT",                                   file.getMetadata().getAdmMessageLink());
916         Assertions.assertEquals("CDM_MSG_35132.TXT",                                   file.getMetadata().getCdmMessageLink());
917         Assertions.assertEquals("PRM_MSG_35132.TXT",                                   file.getMetadata().getPrmMessageLink());
918         Assertions.assertEquals("RDM_MSG_35132.TXT",                                   file.getMetadata().getRdmMessageLink());
919         Assertions.assertEquals("COMSPOC",                                             file.getMetadata().getCatalogName());
920         Assertions.assertEquals("INTELSAT",                                            file.getMetadata().getOperator());
921         Assertions.assertEquals("SIRIUS",                                              file.getMetadata().getOwner());
922         Assertions.assertEquals("FRANCE",                                              file.getMetadata().getCountry());
923         Assertions.assertEquals("SPIRE",                                               file.getMetadata().getConstellation());
924         Assertions.assertEquals("PAYLOAD",                                             file.getMetadata().getObjectType().toString());
925         Assertions.assertEquals("Operational",                                         file.getMetadata().getOpsStatus().toString());
926         Assertions.assertEquals("Extended Geostationary Orbit",                        file.getMetadata().getOrbitCategory().toString());
927         Assertions.assertEquals(7,                                                     file.getMetadata().getOcmDataElements().size());
928         Assertions.assertEquals(OcmElements.ORB,                                       file.getMetadata().getOcmDataElements().get(0));
929         Assertions.assertEquals(OcmElements.PHYS,                                      file.getMetadata().getOcmDataElements().get(1));
930         Assertions.assertEquals(OcmElements.COV,                                       file.getMetadata().getOcmDataElements().get(2));
931         Assertions.assertEquals(OcmElements.MAN,                                       file.getMetadata().getOcmDataElements().get(3));
932         Assertions.assertEquals(OcmElements.PERT,                                      file.getMetadata().getOcmDataElements().get(4));
933         Assertions.assertEquals(OcmElements.OD,                                        file.getMetadata().getOcmDataElements().get(5));
934         Assertions.assertEquals(OcmElements.USER,                                      file.getMetadata().getOcmDataElements().get(6));
935         Assertions.assertEquals("UTC",                                                 file.getMetadata().getTimeSystem().name());
936         final AbsoluteDate epoch = file.getMetadata().getEpochT0();
937         Assertions.assertEquals(new AbsoluteDate(2019, 7, 23,  0, 0, 0.0, TimeScalesFactory.getUTC()), epoch);
938         Assertions.assertEquals(28800.0, file.getMetadata().getSclkOffsetAtEpoch(), 1.0e-10);
939         Assertions.assertEquals(2.5,     file.getMetadata().getSclkSecPerSISec(), 1.0e-15);
940         Assertions.assertEquals(new AbsoluteDate(2019, 7, 23,  9, 29,
941                                                  new TimeOffset(31, TimeOffset.SECOND, 576, TimeOffset.MILLISECOND),
942                                                  TimeScalesFactory.getUTC()),
943                                 file.getMetadata().getPreviousMessageEpoch());
944         Assertions.assertEquals(new AbsoluteDate(2019, 7, 23, 11, 29,
945                                                  new TimeOffset(31, TimeOffset.SECOND, 576, TimeOffset.MILLISECOND),
946                                                  TimeScalesFactory.getUTC()),
947                                 file.getMetadata().getNextMessageEpoch());
948         Assertions.assertEquals(new AbsoluteDate(2019, 7, 23,  9, 30,  0.0, TimeScalesFactory.getUTC()),
949                                 file.getMetadata().getStartTime());
950         Assertions.assertEquals(new AbsoluteDate(2019, 7, 23, 10, 29, 50.0, TimeScalesFactory.getUTC()),
951                                 file.getMetadata().getStopTime());
952         Assertions.assertEquals(0.041550925925 * Constants.JULIAN_DAY, file.getMetadata().getTimeSpan(), 1.0e-15);
953         Assertions.assertEquals(37.0,                                  file.getMetadata().getTaimutcT0(), 1.0e-15);
954         Assertions.assertEquals(new AbsoluteDate(2050, 12, 31, 23, 59, 60.0, TimeScalesFactory.getUTC()),
955                                 file.getMetadata().getNextLeapEpoch());
956         Assertions.assertEquals(38.0,                                  file.getMetadata().getNextLeapTaimutc(), 1.0e-15);
957         Assertions.assertEquals(-0.1642060,                            file.getMetadata().getUt1mutcT0(), 1.0e-15);
958         Assertions.assertEquals("IERS",                                file.getMetadata().getEopSource());
959         Assertions.assertEquals("LAGRANGE ORDER 5",                    file.getMetadata().getInterpMethodEOP());
960         Assertions.assertEquals("JPL_DE_430",                          file.getMetadata().getCelestialSource());
961 
962         // check trajectory data
963         Assertions.assertEquals(3, file.getData().getTrajectoryBlocks().size());
964         TrajectoryStateHistory osh0 = file.getData().getTrajectoryBlocks().get(0);
965         Assertions.assertEquals("this is number 1 ORB comment", osh0.getMetadata().getComments().get(0));
966         Assertions.assertEquals("orbit 1",       osh0.getMetadata().getTrajID());
967         Assertions.assertEquals("orbit 0",       osh0.getMetadata().getTrajPrevID());
968         Assertions.assertEquals("orbit 2",       osh0.getMetadata().getTrajNextID());
969         Assertions.assertEquals("DETERMINED_OD", osh0.getMetadata().getTrajBasis());
970         Assertions.assertEquals("OD_17",         osh0.getMetadata().getTrajBasisID());
971         Assertions.assertEquals(InterpolationMethod.HERMITE, osh0.getMetadata().getInterpolationMethod());
972         Assertions.assertEquals(3, osh0.getMetadata().getInterpolationDegree());
973         Assertions.assertEquals("Orekit",        osh0.getMetadata().getPropagator());
974         Assertions.assertEquals(CenterName.EARTH.name(), osh0.getMetadata().getCenter().getName());
975         Assertions.assertEquals(CelestialBodyFrame.TOD, osh0.getMetadata().getTrajReferenceFrame().asCelestialBodyFrame());
976         Assertions.assertEquals(    0.0,   osh0.getMetadata().getTrajFrameEpoch().durationFrom(epoch),    1.0e-12);
977         Assertions.assertEquals(34200.0,   osh0.getMetadata().getUseableStartTime().durationFrom(epoch), 1.0e-12);
978         Assertions.assertEquals(35999.999, osh0.getMetadata().getUseableStopTime().durationFrom(epoch),  1.0e-12);
979         Assertions.assertEquals(17, osh0.getMetadata().getOrbRevNum());
980         Assertions.assertEquals( 1, osh0.getMetadata().getOrbRevNumBasis());
981         Assertions.assertEquals(OrbitElementsType.CARTPVA, osh0.getMetadata().getTrajType());
982         Assertions.assertEquals(9, osh0.getMetadata().getTrajUnits().size());
983         Assertions.assertEquals(Unit.KILOMETRE,  osh0.getMetadata().getTrajUnits().get(0));
984         Assertions.assertEquals(Unit.KILOMETRE,  osh0.getMetadata().getTrajUnits().get(1));
985         Assertions.assertEquals(Unit.KILOMETRE,  osh0.getMetadata().getTrajUnits().get(2));
986         Assertions.assertEquals(Units.KM_PER_S,  osh0.getMetadata().getTrajUnits().get(3));
987         Assertions.assertEquals(Units.KM_PER_S,  osh0.getMetadata().getTrajUnits().get(4));
988         Assertions.assertEquals(Units.KM_PER_S,  osh0.getMetadata().getTrajUnits().get(5));
989         Assertions.assertEquals(Units.KM_PER_S2, osh0.getMetadata().getTrajUnits().get(6));
990         Assertions.assertEquals(Units.KM_PER_S2, osh0.getMetadata().getTrajUnits().get(7));
991         Assertions.assertEquals(Units.KM_PER_S2, osh0.getMetadata().getTrajUnits().get(8));
992         Assertions.assertEquals(3, osh0.getCoordinates().size());
993         Assertions.assertEquals(   0.0, osh0.getCoordinates().get(0).getDate().durationFrom(epoch), 1.0e-10);
994         Assertions.assertEquals( 1.0e6, osh0.getCoordinates().get(0).getPosition().getX(), 1.0e-10);
995         Assertions.assertEquals( 300.0, osh0.getCoordinates().get(1).getDate().durationFrom(epoch), 1.0e-10);
996         Assertions.assertEquals( 3.0e3, osh0.getCoordinates().get(1).getVelocity().getY(), 1.0e-10);
997         Assertions.assertEquals( 600.0, osh0.getCoordinates().get(2).getDate().durationFrom(epoch), 1.0e-10);
998         Assertions.assertEquals(  -6.0, osh0.getCoordinates().get(2).getAcceleration().getZ(), 1.0e-10);
999         TrajectoryStateHistory osh1 = file.getData().getTrajectoryBlocks().get(1);
1000         Assertions.assertEquals("this is number 2 ORB comment", osh1.getMetadata().getComments().get(0));
1001         Assertions.assertEquals("orbit 2",    osh1.getMetadata().getTrajID());
1002         Assertions.assertEquals("orbit 1",    osh1.getMetadata().getTrajPrevID());
1003         Assertions.assertEquals("orbit 3",    osh1.getMetadata().getTrajNextID());
1004         Assertions.assertEquals("PREDICTED",  osh1.getMetadata().getTrajBasis());
1005         Assertions.assertEquals("SIMULATION", osh1.getMetadata().getTrajBasisID());
1006         Assertions.assertEquals(-1,           osh1.getMetadata().getOrbRevNum());
1007         Assertions.assertEquals(-1,           osh1.getMetadata().getOrbRevNumBasis());
1008         Assertions.assertEquals(3, osh1.getCoordinates().size());
1009         Assertions.assertEquals(1800.0, osh1.getCoordinates().get(0).getDate().durationFrom(epoch), 1.0e-10);
1010         Assertions.assertEquals(2100.0, osh1.getCoordinates().get(1).getDate().durationFrom(epoch), 1.0e-10);
1011         Assertions.assertEquals(2400.0, osh1.getCoordinates().get(2).getDate().durationFrom(epoch), 1.0e-10);
1012         TrajectoryStateHistory osh2 = file.getData().getTrajectoryBlocks().get(2);
1013         Assertions.assertEquals("this is number 3 ORB comment", osh2.getMetadata().getComments().get(0));
1014         Assertions.assertEquals("orbit 3",    osh2.getMetadata().getTrajID());
1015         Assertions.assertEquals("orbit 2",    osh2.getMetadata().getTrajPrevID());
1016         Assertions.assertEquals("orbit 4",    osh2.getMetadata().getTrajNextID());
1017         Assertions.assertEquals("SIMULATED",  osh2.getMetadata().getTrajBasis());
1018         Assertions.assertEquals("ce6898a2-db5f-11ec-8d7c-03ee8546e2d3", osh2.getMetadata().getTrajBasisID());
1019         Assertions.assertEquals(-1,           osh2.getMetadata().getOrbRevNum());
1020         Assertions.assertEquals(-1,           osh2.getMetadata().getOrbRevNumBasis());
1021         Assertions.assertEquals("OSCULATING", osh2.getMetadata().getOrbAveraging());
1022         Assertions.assertEquals(3, osh2.getCoordinates().size());
1023         Assertions.assertEquals(2800.0, osh2.getCoordinates().get(0).getDate().durationFrom(epoch), 1.0e-10);
1024         Assertions.assertEquals(3100.0, osh2.getCoordinates().get(1).getDate().durationFrom(epoch), 1.0e-10);
1025         Assertions.assertEquals(3400.0, osh2.getCoordinates().get(2).getDate().durationFrom(epoch), 1.0e-10);
1026 
1027         // check physical data
1028         OrbitPhysicalProperties phys = file.getData().getPhysicBlock();
1029         Assertions.assertEquals("this is PHYS comment", phys.getComments().get(0));
1030         Assertions.assertEquals("AIRBUS",   phys.getManufacturer());
1031         Assertions.assertEquals("EUROSTAR", phys.getBusModel());
1032         Assertions.assertEquals(3,          phys.getDockedWith().size());
1033         Assertions.assertEquals("A1",       phys.getDockedWith().get(0));
1034         Assertions.assertEquals("A2",       phys.getDockedWith().get(1));
1035         Assertions.assertEquals("A3",       phys.getDockedWith().get(2));
1036         Assertions.assertEquals(5.0,        phys.getDragConstantArea(),               1.0e-10);
1037         Assertions.assertEquals(2.1,        phys.getDragCoefficient(),                1.0e-10);
1038         Assertions.assertEquals(0.1,        phys.getDragUncertainty(),                1.0e-10);
1039         Assertions.assertEquals(700.0,      phys.getInitialWetMass(),                 1.0e-10);
1040         Assertions.assertEquals(600.0,      phys.getWetMass(),                        1.0e-10);
1041         Assertions.assertEquals(500.0,      phys.getDryMass(),                        1.0e-10);
1042         Assertions.assertNull(phys.getOebParentFrame().asOrbitRelativeFrame());
1043         Assertions.assertEquals("TOD",      phys.getOebParentFrame().getName());
1044         Assertions.assertEquals(32400.0,    phys.getOebParentFrameEpoch().durationFrom(epoch), 1.0e-10);
1045         Assertions.assertEquals(0.64,       phys.getOebQ().getQ1(),                   1.0e-10);
1046         Assertions.assertEquals(0.48,       phys.getOebQ().getQ2(),                   1.0e-10);
1047         Assertions.assertEquals(0.48,       phys.getOebQ().getQ3(),                   1.0e-10);
1048         Assertions.assertEquals(0.36,       phys.getOebQ().getQ0(),                   1.0e-10);
1049         Assertions.assertEquals(3.0,        phys.getOebMax(),                         1.0e-10);
1050         Assertions.assertEquals(2.0,        phys.getOebIntermediate(),                1.0e-10);
1051         Assertions.assertEquals(1.0,        phys.getOebMin(),                         1.0e-10);
1052         Assertions.assertEquals(2.2,        phys.getOebAreaAlongMax(),                1.0e-10);
1053         Assertions.assertEquals(3.2,        phys.getOebAreaAlongIntermediate(),       1.0e-10);
1054         Assertions.assertEquals(6.2,        phys.getOebAreaAlongMin(),                1.0e-10);
1055         Assertions.assertEquals(4.3,        phys.getMinAreaForCollisionProbability(), 1.0e-10);
1056         Assertions.assertEquals(6.3,        phys.getMaxAreaForCollisionProbability(), 1.0e-10);
1057         Assertions.assertEquals(5.3,        phys.getTypAreaForCollisionProbability(), 1.0e-10);
1058         Assertions.assertEquals(2.4,        phys.getRcs(),                            1.0e-10);
1059         Assertions.assertEquals(1.4,        phys.getMinRcs(),                         1.0e-10);
1060         Assertions.assertEquals(3.4,        phys.getMaxRcs(),                         1.0e-10);
1061         Assertions.assertEquals(3.5,        phys.getSrpConstantArea(),                1.0e-10);
1062         Assertions.assertEquals(1.7,        phys.getSrpCoefficient(),                 1.0e-10);
1063         Assertions.assertEquals(0.2,        phys.getSrpUncertainty(),                 1.0e-10);
1064         Assertions.assertEquals(15.0,       phys.getVmAbsolute(),                     1.0e-10);
1065         Assertions.assertEquals(19.0,       phys.getVmApparentMin(),                  1.0e-10);
1066         Assertions.assertEquals(15.4,       phys.getVmApparent(),                     1.0e-10);
1067         Assertions.assertEquals(14.0,       phys.getVmApparentMax(),                  1.0e-10);
1068         Assertions.assertEquals(0.7,        phys.getReflectance(),                    1.0e-10);
1069         Assertions.assertEquals("THREE_AXIS",      phys.getAttitudeControlMode());
1070         Assertions.assertEquals("REACTION_WHEELS", phys.getAttitudeActuatorType());
1071         Assertions.assertEquals(0.3, FastMath.toDegrees(phys.getAttitudeKnowledgeAccuracy()), 1.0e-10);
1072         Assertions.assertEquals(2.0, FastMath.toDegrees(phys.getAttitudeControlAccuracy()),   1.0e-10);
1073         Assertions.assertEquals(2.3, FastMath.toDegrees(phys.getAttitudePointingAccuracy()),  1.0e-10);
1074         Assertions.assertEquals(20.0,       phys.getManeuversPerYear(),             1.0e-10);
1075         Assertions.assertEquals(6.8,        phys.getMaxThrust(),                    1.0e-10);
1076         Assertions.assertEquals(1900.0,     phys.getBolDv(),                        1.0e-10);
1077         Assertions.assertEquals(200.0,      phys.getRemainingDv(),                  1.0e-10);
1078         Assertions.assertEquals(1000.0,     phys.getInertiaMatrix().getEntry(0, 0), 1.0e-10);
1079         Assertions.assertEquals( 800.0,     phys.getInertiaMatrix().getEntry(1, 1), 1.0e-10);
1080         Assertions.assertEquals( 400.0,     phys.getInertiaMatrix().getEntry(2, 2), 1.0e-10);
1081         Assertions.assertEquals(  20.0,     phys.getInertiaMatrix().getEntry(0, 1), 1.0e-10);
1082         Assertions.assertEquals(  20.0,     phys.getInertiaMatrix().getEntry(1, 0), 1.0e-10);
1083         Assertions.assertEquals(  40.0,     phys.getInertiaMatrix().getEntry(0, 2), 1.0e-10);
1084         Assertions.assertEquals(  40.0,     phys.getInertiaMatrix().getEntry(2, 0), 1.0e-10);
1085         Assertions.assertEquals(  60.0,     phys.getInertiaMatrix().getEntry(1, 2), 1.0e-10);
1086         Assertions.assertEquals(  60.0,     phys.getInertiaMatrix().getEntry(2, 1), 1.0e-10);
1087 
1088         // check covariance data
1089         Assertions.assertEquals(2, file.getData().getCovarianceBlocks().size());
1090         OrbitCovarianceHistory ch0 = file.getData().getCovarianceBlocks().get(0);
1091         Assertions.assertEquals("this is number 1 COV comment", ch0.getMetadata().getComments().get(0));
1092         Assertions.assertEquals("covariance 1", ch0.getMetadata().getCovID());
1093         Assertions.assertEquals("covariance 0", ch0.getMetadata().getCovPrevID());
1094         Assertions.assertEquals("covariance 2", ch0.getMetadata().getCovNextID());
1095         Assertions.assertEquals("EMPIRICAL",    ch0.getMetadata().getCovBasis());
1096         Assertions.assertEquals("basis 1",      ch0.getMetadata().getCovBasisID());
1097         Assertions.assertNull(ch0.getMetadata().getCovReferenceFrame().asOrbitRelativeFrame());
1098         Assertions.assertEquals("MOD",          ch0.getMetadata().getCovReferenceFrame().getName());
1099         Assertions.assertEquals(33000.0,        ch0.getMetadata().getCovFrameEpoch().durationFrom(epoch), 1.0e-10);
1100         Assertions.assertEquals(0.5,            ch0.getMetadata().getCovScaleMin(),   1.0e-10);
1101         Assertions.assertEquals(5.0,            ch0.getMetadata().getCovScaleMax(),   1.0e-10);
1102         Assertions.assertEquals(0.25,           ch0.getMetadata().getCovConfidence(), 1.0e-10);
1103         Assertions.assertEquals(OrbitElementsType.CARTPV, ch0.getMetadata().getCovType());
1104         Assertions.assertEquals(6, ch0.getMetadata().getCovUnits().size());
1105         Assertions.assertEquals(Unit.KILOMETRE,  ch0.getMetadata().getCovUnits().get(0));
1106         Assertions.assertEquals(Unit.KILOMETRE,  ch0.getMetadata().getCovUnits().get(1));
1107         Assertions.assertEquals(Unit.KILOMETRE,  ch0.getMetadata().getCovUnits().get(2));
1108         Assertions.assertEquals(Units.KM_PER_S,  ch0.getMetadata().getCovUnits().get(3));
1109         Assertions.assertEquals(Units.KM_PER_S,  ch0.getMetadata().getCovUnits().get(4));
1110         Assertions.assertEquals(Units.KM_PER_S,  ch0.getMetadata().getCovUnits().get(5));
1111         Assertions.assertEquals(3, ch0.getCovariances().size());
1112         Assertions.assertEquals(   0.0,  ch0.getCovariances().get(0).getDate().durationFrom(epoch), 1.0e-10);
1113         Assertions.assertEquals(1.1e6,   ch0.getCovariances().get(0).getMatrix().getEntry(0, 0),    1.0e-10);
1114         Assertions.assertEquals( 300.0,  ch0.getCovariances().get(1).getDate().durationFrom(epoch), 1.0e-10);
1115         Assertions.assertEquals(13.2e6,  ch0.getCovariances().get(1).getMatrix().getEntry(2, 1),    1.0e-10);
1116         Assertions.assertEquals( 600.0,  ch0.getCovariances().get(2).getDate().durationFrom(epoch), 1.0e-10);
1117         Assertions.assertEquals(26.5e6,  ch0.getCovariances().get(2).getMatrix().getEntry(4, 5),    1.0e-10);
1118         OrbitCovarianceHistory ch1 = file.getData().getCovarianceBlocks().get(1);
1119         Assertions.assertEquals("this is number 2 COV comment", ch1.getMetadata().getComments().get(0));
1120         Assertions.assertEquals("covariance 2", ch1.getMetadata().getCovID());
1121         Assertions.assertEquals("covariance 1", ch1.getMetadata().getCovPrevID());
1122         Assertions.assertEquals("covariance 3", ch1.getMetadata().getCovNextID());
1123         Assertions.assertEquals("SIMULATED",    ch1.getMetadata().getCovBasis());
1124         Assertions.assertEquals("basis 2",      ch1.getMetadata().getCovBasisID());
1125         Assertions.assertEquals(1, ch1.getCovariances().size());
1126         Assertions.assertEquals(1800.0, ch1.getCovariances().get(0).getDate().durationFrom(epoch), 1.0e-10);
1127         Assertions.assertEquals(43.0e6,   ch1.getCovariances().get(0).getMatrix().getEntry(0, 0),    1.0e-10);
1128         Assertions.assertEquals(20.0e6,   ch1.getCovariances().get(0).getMatrix().getEntry(0, 1),    1.0e-10);
1129         Assertions.assertEquals( 6.0e6,   ch1.getCovariances().get(0).getMatrix().getEntry(0, 5),    1.0e-10);
1130         Assertions.assertEquals( 2.0e6,   ch1.getCovariances().get(0).getMatrix().getEntry(4, 3),    1.0e-10);
1131 
1132         // check maneuver data
1133         Assertions.assertEquals(3, file.getData().getManeuverBlocks().size());
1134         OrbitManeuverHistory m0 = file.getData().getManeuverBlocks().get(0);
1135         Assertions.assertEquals("this is number 1 MAN comment",  m0.getMetadata().getComments().get(0));
1136         Assertions.assertEquals("maneuver 1",                    m0.getMetadata().getManID());
1137         Assertions.assertEquals("maneuver 0",                    m0.getMetadata().getManPrevID());
1138         Assertions.assertEquals("maneuver 2",                    m0.getMetadata().getManNextID());
1139         Assertions.assertEquals(ManBasis.DETERMINED_TLM,         m0.getMetadata().getManBasis());
1140         Assertions.assertEquals("TLM 203",                       m0.getMetadata().getManBasisID());
1141         Assertions.assertEquals("THR_02",                        m0.getMetadata().getManDeviceID());
1142         Assertions.assertEquals(-100.0,                          m0.getMetadata().getManPrevEpoch().durationFrom(epoch), 1.0e-10);
1143         Assertions.assertEquals(+100.0,                          m0.getMetadata().getManNextEpoch().durationFrom(epoch), 1.0e-10);
1144         Assertions.assertEquals("ORBIT",                         m0.getMetadata().getManPurpose().get(0));
1145         Assertions.assertEquals("OD_5",                          m0.getMetadata().getManPredSource());
1146         Assertions.assertNull(m0.getMetadata().getManReferenceFrame().asOrbitRelativeFrame());
1147         Assertions.assertEquals("TOD",                           m0.getMetadata().getManReferenceFrame().getName());
1148         Assertions.assertEquals(2.3,                             m0.getMetadata().getManFrameEpoch().durationFrom(epoch), 1.0e-10);
1149         Assertions.assertEquals("MOON",                          m0.getMetadata().getGravitationalAssist().getName());
1150         Assertions.assertEquals(DutyCycleType.TIME,              m0.getMetadata().getDcType());
1151         Assertions.assertEquals(2.0,                             m0.getMetadata().getDcWindowOpen().durationFrom(epoch),  1.0e-10);
1152         Assertions.assertEquals(100.0,                           m0.getMetadata().getDcWindowClose().durationFrom(epoch), 1.0e-10);
1153         Assertions.assertEquals( 5,                              m0.getMetadata().getDcMinCycles());
1154         Assertions.assertEquals(30,                              m0.getMetadata().getDcMaxCycles());
1155         Assertions.assertEquals( 5.0,                            m0.getMetadata().getDcExecStart().durationFrom(epoch),   1.0e-10);
1156         Assertions.assertEquals(95.0,                            m0.getMetadata().getDcExecStop().durationFrom(epoch),    1.0e-10);
1157         Assertions.assertEquals(8000.0,                          m0.getMetadata().getDcRefTime().durationFrom(epoch),     1.0e-10);
1158         Assertions.assertEquals( 10.0,                           m0.getMetadata().getDcTimePulseDuration(),               1.0e-10);
1159         Assertions.assertEquals(200.0,                           m0.getMetadata().getDcTimePulsePeriod(),                 1.0e-10);
1160         Assertions.assertEquals(23,                              m0.getMetadata().getManComposition().size());
1161         Assertions.assertEquals(ManeuverFieldType.TIME_ABSOLUTE, m0.getMetadata().getManComposition().get( 0));
1162         Assertions.assertEquals(ManeuverFieldType.TIME_RELATIVE, m0.getMetadata().getManComposition().get( 1));
1163         Assertions.assertEquals(ManeuverFieldType.MAN_DURA,      m0.getMetadata().getManComposition().get( 2));
1164         Assertions.assertEquals(ManeuverFieldType.DELTA_MASS,    m0.getMetadata().getManComposition().get( 3));
1165         Assertions.assertEquals(ManeuverFieldType.ACC_X,         m0.getMetadata().getManComposition().get( 4));
1166         Assertions.assertEquals(ManeuverFieldType.ACC_Y,         m0.getMetadata().getManComposition().get( 5));
1167         Assertions.assertEquals(ManeuverFieldType.ACC_Z,         m0.getMetadata().getManComposition().get( 6));
1168         Assertions.assertEquals(ManeuverFieldType.ACC_INTERP,    m0.getMetadata().getManComposition().get( 7));
1169         Assertions.assertEquals(ManeuverFieldType.ACC_MAG_SIGMA, m0.getMetadata().getManComposition().get( 8));
1170         Assertions.assertEquals(ManeuverFieldType.ACC_DIR_SIGMA, m0.getMetadata().getManComposition().get( 9));
1171         Assertions.assertEquals(ManeuverFieldType.DV_X,          m0.getMetadata().getManComposition().get(10));
1172         Assertions.assertEquals(ManeuverFieldType.DV_Y,          m0.getMetadata().getManComposition().get(11));
1173         Assertions.assertEquals(ManeuverFieldType.DV_Z,          m0.getMetadata().getManComposition().get(12));
1174         Assertions.assertEquals(ManeuverFieldType.DV_MAG_SIGMA,  m0.getMetadata().getManComposition().get(13));
1175         Assertions.assertEquals(ManeuverFieldType.DV_DIR_SIGMA,  m0.getMetadata().getManComposition().get(14));
1176         Assertions.assertEquals(ManeuverFieldType.THR_X,         m0.getMetadata().getManComposition().get(15));
1177         Assertions.assertEquals(ManeuverFieldType.THR_Y,         m0.getMetadata().getManComposition().get(16));
1178         Assertions.assertEquals(ManeuverFieldType.THR_Z,         m0.getMetadata().getManComposition().get(17));
1179         Assertions.assertEquals(ManeuverFieldType.THR_EFFIC,     m0.getMetadata().getManComposition().get(18));
1180         Assertions.assertEquals(ManeuverFieldType.THR_INTERP,    m0.getMetadata().getManComposition().get(19));
1181         Assertions.assertEquals(ManeuverFieldType.THR_ISP,       m0.getMetadata().getManComposition().get(20));
1182         Assertions.assertEquals(ManeuverFieldType.THR_MAG_SIGMA, m0.getMetadata().getManComposition().get(21));
1183         Assertions.assertEquals(ManeuverFieldType.THR_DIR_SIGMA, m0.getMetadata().getManComposition().get(22));
1184         Assertions.assertEquals(m0.getMetadata().getManComposition().size() - 2, m0.getMetadata().getManUnits().size());
1185         for (int i = 0; i < m0.getMetadata().getManUnits().size(); ++i) {
1186             ManeuverFieldType type = m0.getMetadata().getManComposition().get(i + 2);
1187             Unit              unit = m0.getMetadata().getManUnits().get(i);
1188             Assertions.assertEquals(type.getUnit(), unit);
1189         }
1190         Assertions.assertEquals(2, m0.getManeuvers().size());
1191         Assertions.assertEquals(   0.0,   m0.getManeuvers().get(0).getDate().durationFrom(epoch), 1.0e-10);
1192         Assertions.assertEquals( 200.0,   m0.getManeuvers().get(0).getDuration(),                 1.0e-10);
1193         Assertions.assertEquals(OnOff.ON, m0.getManeuvers().get(0).getAccelerationInterpolation());
1194         Assertions.assertEquals( 600.0,   m0.getManeuvers().get(1).getDate().durationFrom(epoch), 1.0e-10);
1195         Assertions.assertEquals(  -5.0,   m0.getManeuvers().get(1).getDeltaMass(),                1.0e-10);
1196 
1197         OrbitManeuverHistory m1 = file.getData().getManeuverBlocks().get(1);
1198         Assertions.assertEquals("this is number 2 MAN comment",  m1.getMetadata().getComments().get(0));
1199         Assertions.assertEquals("maneuver 2",                    m1.getMetadata().getManID());
1200         Assertions.assertEquals("maneuver 1",                    m1.getMetadata().getManPrevID());
1201         Assertions.assertEquals("maneuver 3",                    m1.getMetadata().getManNextID());
1202         Assertions.assertEquals(ManBasis.PLANNED,                m1.getMetadata().getManBasis());
1203         Assertions.assertEquals("analysis 17",                   m1.getMetadata().getManBasisID());
1204         Assertions.assertEquals("THR_07",                        m1.getMetadata().getManDeviceID());
1205         Assertions.assertEquals( 200.0,                          m1.getMetadata().getManPrevEpoch().durationFrom(epoch), 1.0e-10);
1206         Assertions.assertEquals( 300.0,                          m1.getMetadata().getManNextEpoch().durationFrom(epoch), 1.0e-10);
1207         Assertions.assertEquals("PERIOD",                        m1.getMetadata().getManPurpose().get(0));
1208         Assertions.assertEquals("OD_5",                          m1.getMetadata().getManPredSource());
1209         Assertions.assertEquals(OrbitRelativeFrame.TNW_INERTIAL, m1.getMetadata().getManReferenceFrame().asOrbitRelativeFrame());
1210         Assertions.assertEquals(0.0,                             m1.getMetadata().getManFrameEpoch().durationFrom(epoch), 1.0e-10);
1211         Assertions.assertEquals("EARTH",                         m1.getMetadata().getGravitationalAssist().getName());
1212         Assertions.assertEquals(DutyCycleType.TIME_AND_ANGLE,    m1.getMetadata().getDcType());
1213         Assertions.assertEquals(1002.0,                          m1.getMetadata().getDcWindowOpen().durationFrom(epoch),  1.0e-10);
1214         Assertions.assertEquals(1100.0,                          m1.getMetadata().getDcWindowClose().durationFrom(epoch), 1.0e-10);
1215         Assertions.assertEquals(14,                              m1.getMetadata().getDcMinCycles());
1216         Assertions.assertEquals(60,                              m1.getMetadata().getDcMaxCycles());
1217         Assertions.assertEquals(1005.0,                          m1.getMetadata().getDcExecStart().durationFrom(epoch),   1.0e-10);
1218         Assertions.assertEquals(1095.0,                          m1.getMetadata().getDcExecStop().durationFrom(epoch),    1.0e-10);
1219         Assertions.assertEquals(12000.0,                         m1.getMetadata().getDcRefTime().durationFrom(epoch),     1.0e-10);
1220         Assertions.assertEquals( 20.0,                           m1.getMetadata().getDcTimePulseDuration(),               1.0e-10);
1221         Assertions.assertEquals(400.0,                           m1.getMetadata().getDcTimePulsePeriod(),                 1.0e-10);
1222         Assertions.assertEquals(22,                              m1.getMetadata().getManComposition().size());
1223         Assertions.assertEquals(0.0,
1224                             Vector3D.distance(m1.getMetadata().getDcRefDir(),
1225                                               Vector3D.PLUS_I),
1226                             1.0e-10);
1227         Assertions.assertEquals(SpacecraftBodyFrame.BaseEquipment.SENSOR, m1.getMetadata().getDcBodyFrame().getBaseEquipment());
1228         Assertions.assertEquals("3",                             m1.getMetadata().getDcBodyFrame().getLabel());
1229         Assertions.assertEquals(0.0,
1230                             Vector3D.distance(m1.getMetadata().getDcBodyTrigger(),
1231                                               new Vector3D(0.707, 0.0, 0.707)),
1232                             1.0e-10);
1233         Assertions.assertEquals(25.0, FastMath.toDegrees(m1.getMetadata().getDcPhaseStartAngle()), 1.0e-10);
1234         Assertions.assertEquals(35.0, FastMath.toDegrees(m1.getMetadata().getDcPhaseStopAngle()),  1.0e-10);
1235         Assertions.assertEquals(22,                              m1.getMetadata().getManComposition().size());
1236         Assertions.assertEquals(ManeuverFieldType.TIME_RELATIVE, m1.getMetadata().getManComposition().get( 0));
1237         Assertions.assertEquals(ManeuverFieldType.MAN_DURA,      m1.getMetadata().getManComposition().get( 1));
1238         Assertions.assertEquals(ManeuverFieldType.DELTA_MASS,    m1.getMetadata().getManComposition().get( 2));
1239         Assertions.assertEquals(ManeuverFieldType.ACC_X,         m1.getMetadata().getManComposition().get( 3));
1240         Assertions.assertEquals(ManeuverFieldType.ACC_Y,         m1.getMetadata().getManComposition().get( 4));
1241         Assertions.assertEquals(ManeuverFieldType.ACC_Z,         m1.getMetadata().getManComposition().get( 5));
1242         Assertions.assertEquals(ManeuverFieldType.ACC_INTERP,    m1.getMetadata().getManComposition().get( 6));
1243         Assertions.assertEquals(ManeuverFieldType.ACC_MAG_SIGMA, m1.getMetadata().getManComposition().get( 7));
1244         Assertions.assertEquals(ManeuverFieldType.ACC_DIR_SIGMA, m1.getMetadata().getManComposition().get( 8));
1245         Assertions.assertEquals(ManeuverFieldType.DV_X,          m1.getMetadata().getManComposition().get( 9));
1246         Assertions.assertEquals(ManeuverFieldType.DV_Y,          m1.getMetadata().getManComposition().get(10));
1247         Assertions.assertEquals(ManeuverFieldType.DV_Z,          m1.getMetadata().getManComposition().get(11));
1248         Assertions.assertEquals(ManeuverFieldType.DV_MAG_SIGMA,  m1.getMetadata().getManComposition().get(12));
1249         Assertions.assertEquals(ManeuverFieldType.DV_DIR_SIGMA,  m1.getMetadata().getManComposition().get(13));
1250         Assertions.assertEquals(ManeuverFieldType.THR_X,         m1.getMetadata().getManComposition().get(14));
1251         Assertions.assertEquals(ManeuverFieldType.THR_Y,         m1.getMetadata().getManComposition().get(15));
1252         Assertions.assertEquals(ManeuverFieldType.THR_Z,         m1.getMetadata().getManComposition().get(16));
1253         Assertions.assertEquals(ManeuverFieldType.THR_EFFIC,     m1.getMetadata().getManComposition().get(17));
1254         Assertions.assertEquals(ManeuverFieldType.THR_INTERP,    m1.getMetadata().getManComposition().get(18));
1255         Assertions.assertEquals(ManeuverFieldType.THR_ISP,       m1.getMetadata().getManComposition().get(19));
1256         Assertions.assertEquals(ManeuverFieldType.THR_MAG_SIGMA, m1.getMetadata().getManComposition().get(20));
1257         Assertions.assertEquals(ManeuverFieldType.THR_DIR_SIGMA, m1.getMetadata().getManComposition().get(21));
1258         Assertions.assertEquals(m1.getMetadata().getManComposition().size() - 1, m1.getMetadata().getManUnits().size());
1259         for (int i = 0; i < m1.getMetadata().getManUnits().size(); ++i) {
1260             ManeuverFieldType type = m1.getMetadata().getManComposition().get(i + 1);
1261             Unit              unit = m1.getMetadata().getManUnits().get(i);
1262             Assertions.assertEquals(type.getUnit(), unit);
1263         }
1264         Assertions.assertEquals(2, m1.getManeuvers().size());
1265         Assertions.assertEquals(1000.0,    m1.getManeuvers().get(0).getDate().durationFrom(epoch), 1.0e-10);
1266         Assertions.assertEquals(0.02,      m1.getManeuvers().get(0).getThrustMagnitudeSigma(),     1.0e-10);
1267         Assertions.assertEquals(4.3,       FastMath.toDegrees(m1.getManeuvers().get(0).getThrustDirectionSigma()), 1.0e-10);
1268         Assertions.assertEquals(OnOff.OFF, m1.getManeuvers().get(0).getAccelerationInterpolation());
1269         Assertions.assertEquals(1600.0,    m1.getManeuvers().get(1).getDate().durationFrom(epoch), 1.0e-10);
1270         Assertions.assertEquals( 25.0,     m1.getManeuvers().get(1).getDv().getX(),                1.0e-10);
1271 
1272 
1273         OrbitManeuverHistory m2 = file.getData().getManeuverBlocks().get(2);
1274         Assertions.assertEquals("this is number 3 MAN comment",     m2.getMetadata().getComments().get(0));
1275         Assertions.assertEquals("maneuver 3",                       m2.getMetadata().getManID());
1276         Assertions.assertEquals("DEPLOYMENT",                       m2.getMetadata().getManDeviceID());
1277         Assertions.assertEquals(10,                                 m2.getMetadata().getManComposition().size());
1278         Assertions.assertEquals(ManeuverFieldType.TIME_ABSOLUTE,    m2.getMetadata().getManComposition().get( 0));
1279         Assertions.assertEquals(ManeuverFieldType.DEPLOY_ID,        m2.getMetadata().getManComposition().get( 1));
1280         Assertions.assertEquals(ManeuverFieldType.DEPLOY_DV_X,      m2.getMetadata().getManComposition().get( 2));
1281         Assertions.assertEquals(ManeuverFieldType.DEPLOY_DV_Y,      m2.getMetadata().getManComposition().get( 3));
1282         Assertions.assertEquals(ManeuverFieldType.DEPLOY_DV_Z,      m2.getMetadata().getManComposition().get( 4));
1283         Assertions.assertEquals(ManeuverFieldType.DEPLOY_MASS,      m2.getMetadata().getManComposition().get( 5));
1284         Assertions.assertEquals(ManeuverFieldType.DEPLOY_DV_SIGMA,  m2.getMetadata().getManComposition().get( 6));
1285         Assertions.assertEquals(ManeuverFieldType.DEPLOY_DIR_SIGMA, m2.getMetadata().getManComposition().get( 7));
1286         Assertions.assertEquals(ManeuverFieldType.DEPLOY_DV_RATIO,  m2.getMetadata().getManComposition().get( 8));
1287         Assertions.assertEquals(ManeuverFieldType.DEPLOY_DV_CDA,    m2.getMetadata().getManComposition().get( 9));
1288         Assertions.assertEquals(m2.getMetadata().getManComposition().size() - 1, m2.getMetadata().getManUnits().size());
1289         for (int i = 0; i < m2.getMetadata().getManUnits().size(); ++i) {
1290             ManeuverFieldType type = m2.getMetadata().getManComposition().get(i + 1);
1291             Unit              unit = m2.getMetadata().getManUnits().get(i);
1292             Assertions.assertEquals(type.getUnit(), unit);
1293         }
1294         Assertions.assertEquals(8, m2.getManeuvers().size());
1295         Assertions.assertEquals(35100.0,   m2.getManeuvers().get(0).getDate().durationFrom(epoch), 1.0e-10);
1296         Assertions.assertEquals("BEE_1",   m2.getManeuvers().get(0).getDeployId());
1297         Assertions.assertEquals(35160.0,   m2.getManeuvers().get(1).getDate().durationFrom(epoch), 1.0e-10);
1298         Assertions.assertEquals(    0.3,   m2.getManeuvers().get(1).getDeployDv().getY(),          1.0e-10);
1299 
1300         // check perturbation data
1301         final Perturbations pert = file.getData().getPerturbationsBlock();
1302         Assertions.assertEquals("this is PERT comment",         pert.getComments().get(0));
1303         Assertions.assertEquals("NRLMSIS00",                    pert.getAtmosphericModel());
1304         Assertions.assertEquals("TEG-4",                        pert.getGravityModel());
1305         Assertions.assertEquals(36,                             pert.getGravityDegree());
1306         Assertions.assertEquals(12,                             pert.getGravityOrder());
1307         Assertions.assertEquals(6378137.0,                      pert.getEquatorialRadius(), 1.0e-9);
1308         Assertions.assertEquals(3.986004415e14,                 pert.getGm(),               1.0);
1309         Assertions.assertEquals("MOON",                         pert.getNBodyPerturbations().get(0).getName());
1310         Assertions.assertEquals("SUN",                          pert.getNBodyPerturbations().get(1).getName());
1311         Assertions.assertEquals(4.17807421629e-3,               FastMath.toDegrees(pert.getCentralBodyRotation()), 1.0e-12);
1312         Assertions.assertEquals(0.00335281066475,               pert.getOblateFlattening(), 1.0e-12);
1313         Assertions.assertEquals("SEMI-DIURNAL",                 pert.getOceanTidesModel());
1314         Assertions.assertEquals("DIURNAL",                      pert.getSolidTidesModel());
1315         Assertions.assertEquals("IERS2010",                     pert.getReductionTheory());
1316         Assertions.assertEquals("KNOCKE",                       pert.getAlbedoModel());
1317         Assertions.assertEquals(100,                            pert.getAlbedoGridSize());
1318         Assertions.assertEquals(ShadowModel.DUAL_CONE,          pert.getShadowModel());
1319         Assertions.assertEquals("EARTH",                        pert.getShadowBodies().get(0).getName());
1320         Assertions.assertEquals("MOON",                         pert.getShadowBodies().get(1).getName());
1321         Assertions.assertEquals("BOX_WING",                     pert.getSrpModel());
1322         Assertions.assertEquals("CELESTRAK",                    pert.getSpaceWeatherSource());
1323         Assertions.assertEquals(0.0, pert.getSpaceWeatherEpoch().durationFrom(new AbsoluteDate(2019, 7, 22, TimeScalesFactory.getUTC())), 1.0e-10);
1324         Assertions.assertEquals("LAGRANGE_ORDER_5",             pert.getInterpMethodSW());
1325         Assertions.assertEquals(3.2e-9,                         pert.getFixedGeomagneticKp(),  1.0e-20);
1326         Assertions.assertEquals(2.1e-9,                         pert.getFixedGeomagneticAp(),  1.0e-20);
1327         Assertions.assertEquals(-20.0e-9,                       pert.getFixedGeomagneticDst(), 1.0e-20);
1328         Assertions.assertEquals(1.20e-20,                       pert.getFixedF10P7(),          1.0e-30);
1329         Assertions.assertEquals(1.32e-20,                       pert.getFixedF10P7Mean(),      1.0e-30);
1330         Assertions.assertEquals(1.30e-20,                       pert.getFixedM10P7(),          1.0e-30);
1331         Assertions.assertEquals(1.42e-20,                       pert.getFixedM10P7Mean(),      1.0e-30);
1332         Assertions.assertEquals(1.40e-20,                       pert.getFixedS10P7(),          1.0e-30);
1333         Assertions.assertEquals(1.52e-20,                       pert.getFixedS10P7Mean(),      1.0e-30);
1334         Assertions.assertEquals(1.50e-20,                       pert.getFixedY10P7(),          1.0e-30);
1335         Assertions.assertEquals(1.62e-20,                       pert.getFixedY10P7Mean(),      1.0e-30);
1336 
1337         // check orbit determination data
1338         OrbitDetermination od = file.getData().getOrbitDeterminationBlock();
1339         Assertions.assertEquals("this is OD comment", od.getComments().get(0));
1340         Assertions.assertEquals("OD_24",              od.getId());
1341         Assertions.assertEquals("OD_23",              od.getPrevId());
1342         Assertions.assertEquals("BWLS",               od.getMethod().getName());
1343         Assertions.assertEquals(OdMethodType.BWLS,    od.getMethod().getType());
1344         Assertions.assertEquals("OREKIT",             od.getMethod().getTool());
1345         Assertions.assertEquals(0.0,
1346                             od.getEpoch().durationFrom(new AbsoluteDate(2019, 7, 22, 17, 32, 27.0,
1347                                                                         TimeScalesFactory.getUTC())),
1348                             1.0e-10);
1349         Assertions.assertEquals(302400.0,             od.getTimeSinceFirstObservation(), 1.0e-10);
1350         Assertions.assertEquals(103680.0,             od.getTimeSinceLastObservation(),  1.0e-10);
1351         Assertions.assertEquals(449280.0,             od.getRecommendedOdSpan(),         1.0e-10);
1352         Assertions.assertEquals(198720.0,             od.getActualOdSpan(),              1.0e-10);
1353         Assertions.assertEquals(100,                  od.getObsAvailable());
1354         Assertions.assertEquals( 90,                  od.getObsUsed());
1355         Assertions.assertEquals( 33,                  od.getTracksAvailable());
1356         Assertions.assertEquals( 30,                  od.getTracksUsed());
1357         Assertions.assertEquals(86400.0,              od.getMaximumObsGap(),             1.0e-10);
1358         Assertions.assertEquals(58.73,                od.getEpochEigenMaj(),             1.0e-10);
1359         Assertions.assertEquals(35.7,                 od.getEpochEigenInt(),             1.0e-10);
1360         Assertions.assertEquals(21.5,                 od.getEpochEigenMin(),             1.0e-10);
1361         Assertions.assertEquals(32.5,                 od.getMaxPredictedEigenMaj(),      1.0e-10);
1362         Assertions.assertEquals(22.0,                 od.getMinPredictedEigenMin(),      1.0e-10);
1363         Assertions.assertEquals(0.953,                od.getConfidence(),                1.0e-10);
1364         Assertions.assertEquals(0.857,                od.getGdop(),                      1.0e-10);
1365         Assertions.assertEquals(6,                    od.getSolveN());
1366         Assertions.assertEquals("POS[3]",             od.getSolveStates().get(0));
1367         Assertions.assertEquals("VEL[3]",             od.getSolveStates().get(1));
1368         Assertions.assertEquals(2,                    od.getConsiderN());
1369         Assertions.assertEquals("DRAG",               od.getConsiderParameters().get(0));
1370         Assertions.assertEquals("SRP",                od.getConsiderParameters().get(1));
1371         Assertions.assertEquals(3,                    od.getSensorsN());
1372         Assertions.assertEquals("EGLIN",              od.getSensors().get(0));
1373         Assertions.assertEquals("FYLINGDALES",        od.getSensors().get(1));
1374         Assertions.assertEquals("PLAGNOLE",           od.getSensors().get(2));
1375         Assertions.assertEquals(1.3,                  od.getWeightedRms(), 1.0e-10);
1376         Assertions.assertEquals("RANGE",              od.getDataTypes().get(0));
1377         Assertions.assertEquals("DOPPLER",            od.getDataTypes().get(1));
1378         Assertions.assertEquals("AZEL",               od.getDataTypes().get(2));
1379 
1380         // check user data
1381         Assertions.assertEquals(1, file.getData().getUserDefinedBlock().getParameters().size());
1382         Assertions.assertEquals("OREKIT", file.getData().getUserDefinedBlock().getParameters().get("LIBRARY"));
1383 
1384         Assertions.assertEquals(1, file.getSatellites().size());
1385         OcmSatelliteEphemeris ephemeris = file.getSatellites().get("POLYSAT");
1386         Assertions.assertEquals("POLYSAT", ephemeris.getId());
1387         Assertions.assertEquals(3.986004415e14, ephemeris.getMu(), 1e5);
1388         Assertions.assertEquals(3.0e5, Constants.IERS2003_EARTH_MU - ephemeris.getMu(), 1e0);
1389         Assertions.assertEquals(3, ephemeris.getSegments().size());
1390         List<TimeStampedPVCoordinates> h0 = ephemeris.getSegments().get(0).getCoordinates();
1391         Assertions.assertEquals(3, h0.size());
1392         Assertions.assertEquals(   0.0, h0.get(0).getDate().durationFrom(epoch), 1.0e-10);
1393         Assertions.assertEquals( 300.0, h0.get(1).getDate().durationFrom(epoch), 1.0e-10);
1394         Assertions.assertEquals( 600.0, h0.get(2).getDate().durationFrom(epoch), 1.0e-10);
1395         List<TimeStampedPVCoordinates> h1 = ephemeris.getSegments().get(1).getCoordinates();
1396         Assertions.assertEquals(3, h1.size());
1397         Assertions.assertEquals(1800.0, h1.get(0).getDate().durationFrom(epoch), 1.0e-10);
1398         Assertions.assertEquals(2100.0, h1.get(1).getDate().durationFrom(epoch), 1.0e-10);
1399         Assertions.assertEquals(2400.0, h1.get(2).getDate().durationFrom(epoch), 1.0e-10);
1400 
1401         Assertions.assertEquals(   0.0, ephemeris.getStart().durationFrom(epoch), 10e-10);
1402         Assertions.assertEquals(3400.0, ephemeris.getStop().durationFrom(epoch), 10e-10);
1403 
1404     }
1405 
1406 
1407     /** Unit tests for parsing an OEM with a custom frame mapper. */
1408     @Test
1409     public void testFrameMapper() {
1410         // setup
1411         Frame parent = FramesFactory.getEME2000();
1412         Frame zzrf = new Frame(parent, Transform.IDENTITY, "ZZRF");
1413         TimeScale tai = TimeScalesFactory.getTAI();
1414         AbsoluteDate expectedEpoch = new AbsoluteDate("2021-01-02", tai);
1415         CcsdsFrameMapper mapper = new CcsdsFrameMapper() {
1416             @Override
1417             public Frame buildCcsdsFrame(FrameFacade orientation, AbsoluteDate epoch) {
1418                 if ("ZZRF".equals(orientation.getName()) &&
1419                         expectedEpoch.equals(epoch)) {
1420                     return zzrf;
1421                 }
1422                 throw new IllegalArgumentException(orientation + " " + epoch);
1423             }
1424 
1425             @Override
1426             public Frame buildCcsdsFrame(BodyFacade center,
1427                                          FrameFacade orientation,
1428                                          AbsoluteDate epoch) {
1429                 if ("ZZ".equals(center.getName()) &&
1430                         "ZZRF".equals(orientation.getName()) &&
1431                         expectedEpoch.equals(epoch)) {
1432                     return zzrf;
1433                 }
1434                 throw new IllegalArgumentException(
1435                         center + " " + orientation + " " + epoch);
1436             }
1437 
1438         };
1439         OcmParser parser = new ParserBuilder()
1440                 .withFrameMapper(mapper)
1441                 // mu has to be set, but not used in this test. TODO body mapper to load mu?
1442                 .withMu(12435)
1443                 .buildOcmParser();
1444         String name = "/ccsds/odm/ocm/OCM-unknown-frame-center.txt";
1445         DataSource source =
1446                 new DataSource(name, () -> getClass().getResourceAsStream(name));
1447 
1448         // action
1449         Ocm ocm = parser.parse(source);
1450 
1451         // verify
1452         MatcherAssert.assertThat(ocm.getMetadata().getFrameMapper(),
1453                 Matchers.sameInstance(mapper));
1454         // check each section that uses a reference frame
1455         final OcmData data = ocm.getData();
1456         List<TrajectoryStateHistory> trajectoryBlocks = data.getTrajectoryBlocks();
1457         for (TrajectoryStateHistory trajectoryBlock : trajectoryBlocks) {
1458             MatcherAssert.assertThat(trajectoryBlock.getFrame(),
1459                     Matchers.sameInstance(zzrf));
1460             // zzrf is not "pseudo-inertial", so use closest ancestor
1461             MatcherAssert.assertThat(trajectoryBlock.getInertialFrame(),
1462                     Matchers.sameInstance(parent));
1463             MatcherAssert.assertThat(trajectoryBlock.getBody().getFrame(),
1464                     Matchers.sameInstance(zzrf));
1465         }
1466         MatcherAssert.assertThat(trajectoryBlocks.size(), Matchers.is(1));
1467         final List<OrbitCovarianceHistory> covarianceBlocks = data.getCovarianceBlocks();
1468         for (OrbitCovarianceHistory covarianceBlock : covarianceBlocks) {
1469             OrbitCovarianceHistoryMetadata metadata = covarianceBlock.getMetadata();
1470             MatcherAssert.assertThat(metadata.getCovFrame(),
1471                     Matchers.sameInstance(zzrf));
1472         }
1473         MatcherAssert.assertThat(covarianceBlocks.size(), Matchers.is(1));
1474         final List<OrbitManeuverHistory> maneuverBlocks = data.getManeuverBlocks();
1475         for (OrbitManeuverHistory maneuverBlock : maneuverBlocks) {
1476             final OrbitManeuverHistoryMetadata metadata = maneuverBlock.getMetadata();
1477             MatcherAssert.assertThat(metadata.getManFrame(),
1478                     Matchers.sameInstance(zzrf));
1479         }
1480         MatcherAssert.assertThat(maneuverBlocks.size(), Matchers.is(1));
1481         final OrbitPhysicalProperties physicBlock = data.getPhysicBlock();
1482         MatcherAssert.assertThat(physicBlock.getOebParent(),
1483                 Matchers.sameInstance(zzrf));
1484     }
1485 
1486     /** Test deprecated constructor. Can be removed in 14.0. */
1487     @Test
1488     @Deprecated
1489     public void testDeprecatedConstructor() {
1490         // action
1491         OcmParser actual = new OcmParser(
1492                 null,
1493                 0,
1494                 0,
1495                 true,
1496                 null,
1497                 0,
1498                 null,
1499                 new Function[0]);
1500 
1501         // verify
1502         MatcherAssert.assertThat(actual.getFrameMapper(),
1503                 Matchers.is(new OrekitCcsdsFrameMapper()));
1504     }
1505 
1506 }