1   /* Copyright 2002-2025 CS GROUP
2    * Licensed to CS GROUP (CS) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * CS licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.orekit.files.ccsds.ndm.odm.opm;
18  
19  import org.hamcrest.MatcherAssert;
20  import org.hipparchus.geometry.euclidean.threed.Vector3D;
21  import org.hipparchus.linear.Array2DRowRealMatrix;
22  import org.hipparchus.util.FastMath;
23  import org.junit.jupiter.api.Assertions;
24  import org.junit.jupiter.api.BeforeEach;
25  import org.junit.jupiter.api.Test;
26  import org.orekit.OrekitMatchers;
27  import org.orekit.Utils;
28  import org.orekit.bodies.CelestialBody;
29  import org.orekit.bodies.CelestialBodyFactory;
30  import org.orekit.data.DataSource;
31  import org.orekit.errors.OrekitException;
32  import org.orekit.errors.OrekitIllegalArgumentException;
33  import org.orekit.errors.OrekitMessages;
34  import org.orekit.files.ccsds.definitions.CelestialBodyFrame;
35  import org.orekit.files.ccsds.ndm.ParsedUnitsBehavior;
36  import org.orekit.files.ccsds.ndm.ParserBuilder;
37  import org.orekit.files.ccsds.ndm.WriterBuilder;
38  import org.orekit.files.ccsds.ndm.odm.CartesianCovariance;
39  import org.orekit.files.ccsds.ndm.odm.KeplerianElements;
40  import org.orekit.files.ccsds.ndm.odm.SpacecraftParameters;
41  import org.orekit.files.ccsds.utils.generation.Generator;
42  import org.orekit.files.ccsds.utils.generation.KvnGenerator;
43  import org.orekit.frames.Frame;
44  import org.orekit.frames.FramesFactory;
45  import org.orekit.frames.LOFType;
46  import org.orekit.orbits.PositionAngleType;
47  import org.orekit.time.AbsoluteDate;
48  import org.orekit.time.TimeOffset;
49  import org.orekit.time.TimeScalesFactory;
50  import org.orekit.utils.Constants;
51  import org.orekit.utils.IERSConventions;
52  import org.orekit.utils.PVCoordinates;
53  import org.orekit.utils.TimeStampedPVCoordinates;
54  
55  import java.io.ByteArrayInputStream;
56  import java.io.CharArrayWriter;
57  import java.io.IOException;
58  import java.net.URISyntaxException;
59  import java.nio.charset.StandardCharsets;
60  import java.util.ArrayList;
61  import java.util.HashMap;
62  
63  public class OpmParserTest {
64  
65      @BeforeEach
66      public void setUp() {
67          Utils.setDataRoot("regular-data");
68      }
69  
70      @Test
71      public void testParseOPM1KVN() {
72          // simple test for OPM file, contains p/v entries and other mandatory
73          // data.
74          final String ex = "/ccsds/odm/opm/OPMExample1.txt";
75          final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
76  
77          final OpmParser parser = new ParserBuilder().withMu(398600e9).withDefaultMass(1000.0).buildOpmParser();
78  
79          final Opm file = parser.parseMessage(source);
80          Assertions.assertEquals(IERSConventions.IERS_2010, file.getConventions());
81  
82          // Check Header Block;
83          Assertions.assertEquals(3.0, file.getHeader().getFormatVersion(), 1.0e-10);
84          Assertions.assertEquals(new AbsoluteDate(1998, 11, 6, 9, 23, 57,
85                                               TimeScalesFactory.getUTC()),
86                              file.getHeader().getCreationDate());
87          Assertions.assertEquals("JAXA", file.getHeader().getOriginator());
88  
89          // Check Metadata Block;
90  
91          Assertions.assertEquals("GODZILLA 5", file.getMetadata().getObjectName());
92          Assertions.assertEquals("1998-999A", file.getMetadata().getObjectID());
93          Assertions.assertEquals(1998, file.getMetadata().getLaunchYear());
94          Assertions.assertEquals(999, file.getMetadata().getLaunchNumber());
95          Assertions.assertEquals("A", file.getMetadata().getLaunchPiece());
96          Assertions.assertEquals("EARTH", file.getMetadata().getCenter().getName());
97          Assertions.assertNotNull(file.getMetadata().getCenter().getBody());
98          Assertions.assertEquals(CelestialBodyFactory.getEarth(), file.getMetadata().getCenter().getBody());
99          Assertions.assertEquals(CelestialBodyFrame.ITRF2000, CelestialBodyFrame.map(file.getMetadata().getFrame()));
100         Assertions.assertEquals("UTC", file.getMetadata().getTimeSystem().name());
101         Assertions.assertNull(file.getData().getCovarianceBlock());
102 
103         // Check State Vector data Block;
104         Assertions.assertEquals(new AbsoluteDate(1998, 12, 18, 14, 28,
105                                                  new TimeOffset(15, TimeOffset.SECOND, 117200, TimeOffset.MICROSECOND),
106                                                  TimeScalesFactory.getUTC()),
107                             file.getDate());
108         checkPVEntry(new PVCoordinates(new Vector3D(6503514.000, 1239647.000, -717490.000),
109                                        new Vector3D(-873.160, 8740.420, -4191.076)),
110                      file.getPVCoordinates());
111 
112         try {
113             file.generateCartesianOrbit();
114             Assertions.fail("an exception should have been thrown");
115         } catch(OrekitIllegalArgumentException oiae) {
116             Assertions.assertEquals(OrekitMessages.NON_PSEUDO_INERTIAL_FRAME, oiae.getSpecifier());
117             Assertions.assertEquals("ITRF-2000/CIO/2010-based ITRF simple EOP", oiae.getParts()[0]);
118         }
119         try {
120             file.generateKeplerianOrbit();
121             Assertions.fail("an exception should have been thrown");
122         } catch(OrekitIllegalArgumentException oiae) {
123             Assertions.assertEquals(OrekitMessages.NON_PSEUDO_INERTIAL_FRAME, oiae.getSpecifier());
124             Assertions.assertEquals("ITRF-2000/CIO/2010-based ITRF simple EOP", oiae.getParts()[0]);
125         }
126         try {
127             file.generateSpacecraftState();
128             Assertions.fail("an exception should have been thrown");
129         } catch(OrekitIllegalArgumentException oiae) {
130             Assertions.assertEquals(OrekitMessages.NON_PSEUDO_INERTIAL_FRAME, oiae.getSpecifier());
131             Assertions.assertEquals("ITRF-2000/CIO/2010-based ITRF simple EOP", oiae.getParts()[0]);
132         }
133 
134     }
135 
136     @Test
137     public void testParseOPM2() {
138         // simple test for OPM file, contains all mandatory information plus
139         // Keplerian elements, Spacecraft parameters and 2 maneuvers.
140         final String ex = "/ccsds/odm/opm/OPMExample2.txt";
141         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
142 
143         final OpmParser parser = new ParserBuilder().withMu(Constants.EIGEN5C_EARTH_MU).withDefaultMass(1000.0).buildOpmParser();
144         final Opm file = parser.parseMessage(source);
145         Assertions.assertEquals(IERSConventions.IERS_2010, file.getConventions());
146 
147         // Check Header Block;
148         Assertions.assertEquals(3.0, file.getHeader().getFormatVersion(), 1.0e-10);
149         ArrayList<String> headerComment = new ArrayList<>();
150         headerComment.add("Generated by GSOC, R. Kiehling");
151         headerComment.add("Current intermediate orbit IO2 and maneuver planning data");
152         Assertions.assertEquals(headerComment, file.getHeader().getComments());
153         Assertions.assertEquals(new AbsoluteDate(2000, 6, 3, 5, 33, 0,
154                                              TimeScalesFactory.getUTC()),
155                             file.getHeader().getCreationDate());
156         Assertions.assertEquals(file.getHeader().getOriginator(), "GSOC");
157 
158         // Check Metadata Block;
159 
160         Assertions.assertEquals("EUTELSAT W4", file.getMetadata().getObjectName());
161         Assertions.assertEquals("2000-028A", file.getMetadata().getObjectID());
162         Assertions.assertEquals("EARTH", file.getMetadata().getCenter().getName());
163         Assertions.assertNotNull(file.getMetadata().getCenter().getBody());
164         Assertions.assertEquals(CelestialBodyFactory.getEarth(), file.getMetadata().getCenter().getBody());
165         Assertions.assertEquals(FramesFactory.getTOD(IERSConventions.IERS_2010, true),
166                             file.getMetadata().getFrame());
167         Assertions.assertEquals("UTC", file.getMetadata().getTimeSystem().name());
168         Assertions.assertEquals(0, file.getMetadata().getComments().size());
169 
170         // Check Data State Vector block
171         ArrayList<String> epochComment = new ArrayList<>();
172         epochComment.add("State Vector");
173         Assertions.assertEquals(epochComment, file.getData().getStateVectorBlock().getComments());
174         Assertions.assertEquals(new AbsoluteDate(2006, 6, 3, 0, 0, 0,
175                                              TimeScalesFactory.getUTC()),
176                             file.getDate());
177         checkPVEntry(new PVCoordinates(new Vector3D(6655994.2, -40218575.1, -82917.7),
178                                        new Vector3D(3115.48208, 470.42605, -1.01495)),
179                      file.getPVCoordinates());
180 
181         // Check Data Keplerian Elements block
182         KeplerianElements keplerianElements = file.getData().getKeplerianElementsBlock();
183         Assertions.assertNotNull(keplerianElements);
184         ArrayList<String> keplerianElementsComment = new ArrayList<>();
185         keplerianElementsComment.add("Keplerian elements");
186         Assertions.assertEquals(keplerianElementsComment, keplerianElements.getComments());
187         Assertions.assertEquals(41399512.3, keplerianElements.getA(), 1e-6);
188         Assertions.assertEquals(0.020842611, keplerianElements.getE(), 1e-10);
189         Assertions.assertEquals(FastMath.toRadians(0.117746), keplerianElements.getI(), 1e-10);
190         Assertions.assertEquals(FastMath.toRadians(17.604721), keplerianElements.getRaan(), 1e-10);
191         Assertions.assertEquals(FastMath.toRadians(218.242943), keplerianElements.getPa(), 1e-10);
192         Assertions.assertEquals(PositionAngleType.TRUE, keplerianElements.getAnomalyType());
193         Assertions.assertEquals(FastMath.toRadians(41.922339), keplerianElements.getAnomaly(), 1e-10);
194         Assertions.assertEquals(398600.4415 * 1e9, keplerianElements.getMu(), 1e-10);
195 
196         // Check Data Spacecraft block
197         SpacecraftParameters spacecraftParameters = file.getData().getSpacecraftParametersBlock();
198         Assertions.assertNotNull(spacecraftParameters);
199         ArrayList<String> spacecraftComment = new ArrayList<>();
200         spacecraftComment.add("Spacecraft parameters");
201         Assertions.assertEquals(spacecraftComment, spacecraftParameters.getComments());
202         Assertions.assertEquals(1913.000, spacecraftParameters.getMass(), 1e-10);
203         Assertions.assertEquals(10.000, spacecraftParameters.getSolarRadArea(), 1e-10);
204         Assertions.assertEquals(1.300, spacecraftParameters.getSolarRadCoeff(), 1e-10);
205         Assertions.assertEquals(10.000, spacecraftParameters.getDragArea(), 1e-10);
206         Assertions.assertEquals(2.300, spacecraftParameters.getDragCoeff(), 1e-10);
207 
208         // Check covariance block
209         Assertions.assertNull(file.getData().getCovarianceBlock());
210 
211         // Check Data Maneuvers block
212         Assertions.assertTrue(file.getData().hasManeuvers());
213         Assertions.assertEquals(2, file.getNbManeuvers());
214         ArrayList<String> stateManeuverComment0 = new ArrayList<>();
215         stateManeuverComment0.add("2 planned maneuvers");
216         stateManeuverComment0.add("First maneuver: AMF-3");
217         stateManeuverComment0.add("Non-impulsive, thrust direction fixed in inertial frame");
218         Assertions.assertEquals(stateManeuverComment0, file.getManeuver(0).getComments());
219         Assertions.assertEquals(new AbsoluteDate(2000, 6, 3, 9, 0,
220                                                  new TimeOffset(34, TimeOffset.SECOND, 100, TimeOffset.MILLISECOND),
221                                                  TimeScalesFactory.getUTC()),
222                             file.getManeuvers().get(0).getEpochIgnition());
223         Assertions.assertEquals(132.6, file.getManeuver(0).getDuration(), 1e-10);
224         Assertions.assertEquals(-18.418, file.getManeuver(0).getDeltaMass(), 1e-10);
225         Assertions.assertNull(file.getManeuver(0).getReferenceFrame().asOrbitRelativeFrame());
226         Assertions.assertEquals(FramesFactory.getEME2000(), file.getManeuver(0).getReferenceFrame().asFrame());
227         Assertions.assertEquals(0.0,
228                             new Vector3D(-23.25700, 16.83160, -8.93444).distance(file.getManeuver(0).getDV()),
229                             1.0e-10);
230 
231         ArrayList<String> stateManeuverComment1 = new ArrayList<>();
232         stateManeuverComment1.add("Second maneuver: first station acquisition maneuver");
233         stateManeuverComment1.add("impulsive, thrust direction fixed in RTN frame");
234         Assertions.assertEquals(stateManeuverComment1, file.getManeuver(1).getComments());
235         Assertions.assertEquals(new AbsoluteDate(2000, 6, 5, 18, 59, 21,
236                                              TimeScalesFactory.getUTC()),
237                             file.getManeuvers().get(1).getEpochIgnition());
238         Assertions.assertEquals(0.0, file.getManeuver(1).getDuration(), 1e-10);
239         Assertions.assertEquals(-1.469, file.getManeuver(1).getDeltaMass(), 1e-10);
240         Assertions.assertEquals(LOFType.QSW, file.getManeuver(1).getReferenceFrame().asOrbitRelativeFrame().getLofType());
241         Assertions.assertNull(file.getManeuver(1).getReferenceFrame().asFrame());
242         Assertions.assertNull(file.getManeuver(1).getReferenceFrame().asCelestialBodyFrame());
243         Assertions.assertEquals(0.0,
244                             new Vector3D(1.015, -1.873, 0.0).distance(file.getManeuver(1).getDV()),
245                             1.0e-10);
246 
247         Assertions.assertNull(file.getData().getUserDefinedBlock());
248         Assertions.assertNotNull(file.generateCartesianOrbit());
249         Assertions.assertNotNull(file.generateKeplerianOrbit());
250         Assertions.assertNotNull(file.generateSpacecraftState());
251 
252     }
253 
254     @Test
255     public void testParseOPM5() {
256         // simple test for OPM file, contains all mandatory information plus
257         // Keplerian elements, Spacecraft parameters and 3 maneuvers.
258         final String ex = "/ccsds/odm/opm/OPMExample5.txt";
259         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
260 
261         final OpmParser parser = new ParserBuilder().withMu(Constants.EIGEN5C_EARTH_MU).withDefaultMass(1000.0).buildOpmParser();
262         final Opm file = parser.parseMessage(source);
263 
264         // Check Header Block;
265         Assertions.assertEquals(2.0, file.getHeader().getFormatVersion(), 1.0e-10);
266         ArrayList<String> headerComment = new ArrayList<>();
267         headerComment.add("Generated by GSOC, R. Kiehling");
268         headerComment.add("Current intermediate orbit IO2 and maneuver planning data");
269         Assertions.assertEquals(headerComment, file.getHeader().getComments());
270         Assertions.assertEquals(new AbsoluteDate(2000, 6, 3, 5, 33, 0,
271                                              TimeScalesFactory.getUTC()),
272                             file.getHeader().getCreationDate());
273         Assertions.assertEquals(file.getHeader().getOriginator(), "GSOC");
274 
275         // Check Metadata Block;
276         Assertions.assertEquals("EUTELSAT W4", file.getMetadata().getObjectName());
277         Assertions.assertEquals("2000-028A", file.getMetadata().getObjectID());
278         Assertions.assertEquals("EARTH", file.getMetadata().getCenter().getName());
279         Assertions.assertNotNull(file.getMetadata().getCenter().getBody());
280         Assertions.assertEquals(CelestialBodyFactory.getEarth(), file.getMetadata().getCenter().getBody());
281         Assertions.assertEquals(FramesFactory.getGCRF(), file.getMetadata().getFrame());
282         Assertions.assertEquals("GPS", file.getMetadata().getTimeSystem().name());
283         Assertions.assertEquals(0, file.getMetadata().getComments().size());
284 
285         // Check Data State Vector block
286         ArrayList<String> stateVectorComment = new ArrayList<>();
287         stateVectorComment.add("State Vector");
288         Assertions.assertEquals(stateVectorComment, file.getData().getStateVectorBlock().getComments());
289         Assertions.assertEquals(new AbsoluteDate(2006, 6, 3, 0, 0, 0,
290                                              TimeScalesFactory.getGPS()),
291                             file.getData().getStateVectorBlock().getEpoch());
292         checkPVEntry(new PVCoordinates(new Vector3D(6655994.2, -40218575.1, -82917.7),
293                                        new Vector3D(3115.48208, 470.42605, -1.01495)),
294                      file.getPVCoordinates());
295 
296         // Check Data Keplerian Elements block
297         KeplerianElements keplerianElements = file.getData().getKeplerianElementsBlock();
298         Assertions.assertNotNull(keplerianElements);
299         ArrayList<String> keplerianElementsComment = new ArrayList<>();
300         keplerianElementsComment.add("Keplerian elements");
301         Assertions.assertEquals(keplerianElementsComment, keplerianElements.getComments());
302         Assertions.assertEquals(41399512.3, keplerianElements.getA(), 1e-6);
303         Assertions.assertEquals(0.020842611, keplerianElements.getE(), 1e-10);
304         Assertions.assertEquals(FastMath.toRadians(0.117746), keplerianElements.getI(), 1e-10);
305         Assertions.assertEquals(FastMath.toRadians(17.604721), keplerianElements.getRaan(), 1e-10);
306         Assertions.assertEquals(FastMath.toRadians(218.242943), keplerianElements.getPa(), 1e-10);
307         Assertions.assertEquals(PositionAngleType.TRUE, keplerianElements.getAnomalyType());
308         Assertions.assertEquals(FastMath.toRadians(41.922339), keplerianElements.getAnomaly(), 1e-10);
309         Assertions.assertEquals(398600.4415 * 1e9, keplerianElements.getMu(), 1e-10);
310 
311         // Check Data Spacecraft block
312         SpacecraftParameters spacecraftParameters = file.getData().getSpacecraftParametersBlock();
313         ArrayList<String> spacecraftComment = new ArrayList<>();
314         spacecraftComment.add("Spacecraft parameters");
315         Assertions.assertEquals(spacecraftComment, spacecraftParameters.getComments());
316         Assertions.assertEquals(1913.000, spacecraftParameters.getMass(), 1e-10);
317         Assertions.assertEquals(10.000, spacecraftParameters.getSolarRadArea(), 1e-10);
318         Assertions.assertEquals(1.300, spacecraftParameters.getSolarRadCoeff(), 1e-10);
319         Assertions.assertEquals(10.000, spacecraftParameters.getDragArea(), 1e-10);
320         Assertions.assertEquals(2.300, spacecraftParameters.getDragCoeff(), 1e-10);
321 
322         // Check covariance block
323         Assertions.assertNull(file.getData().getCovarianceBlock());
324 
325         // Check Data Maneuvers block
326         Assertions.assertTrue(file.getData().hasManeuvers());
327         Assertions.assertEquals(3, file.getNbManeuvers());
328         ArrayList<String> stateManeuverComment0 = new ArrayList<>();
329         stateManeuverComment0.add("2 planned maneuvers");
330         stateManeuverComment0.add("First maneuver: AMF-3");
331         stateManeuverComment0.add("Non-impulsive, thrust direction fixed in inertial frame");
332         Assertions.assertEquals(stateManeuverComment0, file.getManeuver(0).getComments());
333         Assertions.assertEquals(new AbsoluteDate(2000, 6, 3, 9, 0,
334                                                  new TimeOffset(34, TimeOffset.SECOND, 100, TimeOffset.MILLISECOND),
335                                                  TimeScalesFactory.getGPS()),
336                             file.getManeuvers().get(0).getEpochIgnition());
337         Assertions.assertEquals(132.6, file.getManeuver(0).getDuration(), 1e-10);
338         Assertions.assertEquals(-18.418, file.getManeuver(0).getDeltaMass(), 1e-10);
339         Assertions.assertNull(file.getManeuver(0).getReferenceFrame().asOrbitRelativeFrame());
340         Assertions.assertEquals(FramesFactory.getEME2000(), file.getManeuver(0).getReferenceFrame().asFrame());
341         Assertions.assertEquals(0.0,
342                             new Vector3D(-23.25700, 16.83160, -8.93444).distance(file.getManeuver(0).getDV()),
343                             1.0e-10);
344 
345         ArrayList<String> stateManeuverComment1 = new ArrayList<>();
346         stateManeuverComment1.add("Second maneuver: first station acquisition maneuver");
347         stateManeuverComment1.add("impulsive, thrust direction fixed in RTN frame");
348         Assertions.assertEquals(stateManeuverComment1, file.getManeuver(1).getComments());
349         Assertions.assertEquals(new AbsoluteDate(2000, 6, 5, 18, 59, 21, TimeScalesFactory.getGPS()),
350                             file.getManeuvers().get(1).getEpochIgnition());
351         Assertions.assertEquals(0.0, file.getManeuver(1).getDuration(), 1e-10);
352         Assertions.assertEquals(-1.469, file.getManeuver(1).getDeltaMass(), 1e-10);
353         Assertions.assertEquals(LOFType.QSW, file.getManeuver(1).getReferenceFrame().asOrbitRelativeFrame().getLofType());
354         Assertions.assertNull(file.getManeuver(1).getReferenceFrame().asFrame());
355         Assertions.assertNull(file.getManeuver(1).getReferenceFrame().asCelestialBodyFrame());
356         Assertions.assertEquals(0.0,
357                             new Vector3D(1.015, -1.873, 0.0).distance(file.getManeuver(1).getDV()),
358                             1.0e-10);
359 
360         Assertions.assertTrue(file.getManeuver(2).getComments().isEmpty());
361         Assertions.assertEquals(new AbsoluteDate(2000, 6, 5, 18, 59, 51,TimeScalesFactory.getGPS()),
362                             file.getManeuvers().get(2).getEpochIgnition());
363         Assertions.assertEquals(0.0, file.getManeuver(2).getDuration(), 1e-10);
364         Assertions.assertEquals(-1.469, file.getManeuver(2).getDeltaMass(), 1e-10);
365         Assertions.assertEquals(LOFType.QSW, file.getManeuver(2).getReferenceFrame().asOrbitRelativeFrame().getLofType());
366         Assertions.assertNull(file.getManeuver(2).getReferenceFrame().asFrame());
367         Assertions.assertNull(file.getManeuver(2).getReferenceFrame().asCelestialBodyFrame());
368         Assertions.assertEquals(0.0,
369                             new Vector3D(1.015, -1.873, 0.0).distance(file.getManeuver(2).getDV()),
370                             1.0e-10);
371 
372         file.generateCartesianOrbit();
373         file.generateKeplerianOrbit();
374         file.generateSpacecraftState();
375 
376     }
377 
378     @Test
379     public void testParseOPM3KVN() {
380         // simple test for OPM file, contains all mandatory information plus
381         // Spacecraft parameters and the position/velocity Covariance Matrix.
382         final String name = "/ccsds/odm/opm/OPMExample3.txt";
383         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
384         OpmParser parser = new ParserBuilder().withDefaultMass(1000.0).buildOpmParser();
385         final Opm file = parser.parseMessage(source);
386         Assertions.assertEquals("OPM 201113719185", file.getHeader().getMessageId());
387         Assertions.assertEquals(CelestialBodyFrame.TOD, file.getMetadata().getReferenceFrame().asCelestialBodyFrame());
388         Assertions.assertEquals(new AbsoluteDate(1998, 12, 18, 14, 28,
389                                                  new TimeOffset(15, TimeOffset.SECOND, 117200, TimeOffset.MICROSECOND),
390                                                  TimeScalesFactory.getUTC()),
391                             file.getMetadata().getFrameEpoch());
392         Assertions.assertEquals(1, file.getMetadata().getComments().size());
393         Assertions.assertEquals("GEOCENTRIC, CARTESIAN, EARTH FIXED", file.getMetadata().getComments().get(0));
394         Assertions.assertEquals(15951238.3495, file.generateKeplerianOrbit().getA(), 0.001);
395         Assertions.assertEquals(0.5914452565, file.generateKeplerianOrbit().getE(), 1.0e-10);
396         // Check Data Covariance matrix Block
397         CartesianCovariance covariance = file.getData().getCovarianceBlock();
398         Assertions.assertNotNull(covariance);
399         Assertions.assertSame(file.getMetadata().getReferenceFrame(), covariance.getReferenceFrame());
400 
401         Array2DRowRealMatrix covMatrix = new Array2DRowRealMatrix(6, 6);
402         double[] column1 = {
403             333.1349476038534, 461.8927349220216,
404             -307.0007847730449, -0.3349365033922630,
405             -0.2211832501084875, -0.3041346050686871
406         };
407         double[] column2 = {
408             461.8927349220216, 678.2421679971363,
409             -422.1234189514228, -0.4686084221046758,
410             -0.2864186892102733, -0.4989496988610662
411         };
412         double[] column3 = {
413             -307.0007847730449, -422.1234189514228,
414             323.1931992380369, 0.2484949578400095,
415             0.1798098699846038, 0.3540310904497689
416         };
417         double[] column4 = {
418             -0.3349365033922630, -0.4686084221046758,
419             0.2484949578400095, 0.0004296022805587290,
420             0.0002608899201686016, 0.0001869263192954590
421         };
422         double[] column5 = {
423             -0.2211832501084875, -0.2864186892102733,
424             0.1798098699846038, 0.0002608899201686016,
425             0.0001767514756338532, 0.0001008862586240695
426         };
427         double[] column6 = {
428             -0.3041346050686871, -0.4989496988610662,
429             0.3540310904497689, 0.0001869263192954590,
430             0.0001008862586240695, 0.0006224444338635500
431         };
432         covMatrix.setColumn(0, column1);
433         covMatrix.setColumn(1, column2);
434         covMatrix.setColumn(2, column3);
435         covMatrix.setColumn(3, column4);
436         covMatrix.setColumn(4, column5);
437         covMatrix.setColumn(5, column6);
438         for (int i = 0; i < 6; i++) {
439             for (int j = 0; j < 6; j++) {
440                 Assertions.assertEquals(covMatrix.getEntry(i, j),
441                                     covariance.getCovarianceMatrix().getEntry(i, j),
442                                     1e-15);
443             }
444         }
445 
446     }
447 
448     @Test
449     public void testParseOPM3XML() {
450         // simple test for OPM file, contains all mandatory information plus
451         // Spacecraft parameters and the position/velocity Covariance Matrix.
452         // the content of the file is slightly different from the KVN file in the covariance section
453         final String name = "/ccsds/odm/opm/OPMExample3.xml";
454         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
455         OpmParser parser = new ParserBuilder().withDefaultMass(1000.0).buildOpmParser();
456         validateOPM3XML(parser.parseMessage(source));
457     }
458 
459     @Test
460     public void testWriteOPM3() throws IOException {
461         // simple test for OPM file, contains all mandatory information plus
462         // Spacecraft parameters and the position/velocity Covariance Matrix.
463         // the content of the file is slightly different from the KVN file in the covariance section
464         final String name = "/ccsds/odm/opm/OPMExample3.xml";
465         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
466         OpmParser parser = new ParserBuilder().withDefaultMass(1000.0).buildOpmParser();
467         final Opm original = parser.parseMessage(source);
468 
469         // write the parsed file back to a characters array
470         final CharArrayWriter caw = new CharArrayWriter();
471         final Generator generator = new KvnGenerator(caw, OpmWriter.KVN_PADDING_WIDTH, "dummy",
472                                                      Constants.JULIAN_DAY, 60);
473         new WriterBuilder().buildOpmWriter().writeMessage(generator, original);
474 
475         // reparse the written file
476         final byte[]     bytes   = caw.toString().getBytes(StandardCharsets.UTF_8);
477         final DataSource source2 = new DataSource(name, () -> new ByteArrayInputStream(bytes));
478         final Opm    rebuilt = new ParserBuilder().buildOpmParser().parseMessage(source2);
479         validateOPM3XML(rebuilt);
480 
481     }
482 
483     private void validateOPM3XML(final Opm file) {
484         Assertions.assertEquals("OPM 201113719185", file.getHeader().getMessageId());
485         Assertions.assertEquals(CelestialBodyFrame.TOD, file.getMetadata().getReferenceFrame().asCelestialBodyFrame());
486         Assertions.assertEquals(new AbsoluteDate(1998, 12, 18, 14, 28, new TimeOffset(15, TimeOffset.SECOND, 117200, TimeOffset.MICROSECOND),
487                                              TimeScalesFactory.getUTC()),
488                             file.getMetadata().getFrameEpoch());
489         Assertions.assertEquals(1, file.getMetadata().getComments().size());
490         Assertions.assertEquals("GEOCENTRIC, CARTESIAN, EARTH FIXED", file.getMetadata().getComments().get(0));
491         Assertions.assertEquals(15951238.3495, file.generateKeplerianOrbit().getA(), 0.001);
492         Assertions.assertEquals(0.5914452565, file.generateKeplerianOrbit().getE(), 1.0e-10);
493         // Check Data Covariance matrix Block
494         CartesianCovariance covariance = file.getData().getCovarianceBlock();
495         Assertions.assertNotNull(covariance);
496         Assertions.assertEquals(CelestialBodyFrame.ITRF1997, covariance.getReferenceFrame().asCelestialBodyFrame());
497 
498         Array2DRowRealMatrix covMatrix = new Array2DRowRealMatrix(6, 6);
499         double[] column1 = {
500             316000.0, 722000.0, 202000.0, 912000.0, 562000.0, 245000.0
501         };
502         double[] column2 = {
503             722000.0, 518000.0, 715000.0, 306000.0, 899000.0, 965000.0
504         };
505         double[] column3 = {
506             202000.0, 715000.0, 002000.0, 276000.0, 022000.0, 950000.0
507         };
508         double[] column4 = {
509             912000.0, 306000.0, 276000.0, 797000.0, 079000.0, 435000.0
510         };
511         double[] column5 = {
512             562000.0, 899000.0, 022000.0, 079000.0, 415000.0, 621000.0
513         };
514         double[] column6 = {
515             245000.0, 965000.0, 950000.0, 435000.0, 621000.0, 991000.0
516         };
517         covMatrix.setColumn(0, column1);
518         covMatrix.setColumn(1, column2);
519         covMatrix.setColumn(2, column3);
520         covMatrix.setColumn(3, column4);
521         covMatrix.setColumn(4, column5);
522         covMatrix.setColumn(5, column6);
523         for (int i = 0; i < 6; i++) {
524             for (int j = 0; j < 6; j++) {
525                 Assertions.assertEquals(covMatrix.getEntry(i, j),
526                                     covariance.getCovarianceMatrix().getEntry(i, j),
527                                     1e-15);
528             }
529         }
530 
531     }
532 
533     @Test
534     public void testParseOPM3NoDesignator() {
535         final String ex = "/ccsds/odm/opm/OPM-no-designator.txt";
536         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
537         OpmParser parser = new ParserBuilder().withMu(Constants.EIGEN5C_EARTH_MU).withDefaultMass(1000.0).buildOpmParser();
538         final Opm file = parser.parseMessage(source);
539         Assertions.assertEquals(new AbsoluteDate(1998, 12, 18, 14, 28,
540                                                  new TimeOffset(15, TimeOffset.SECOND, 117200, TimeOffset.MICROSECOND),
541                                                  TimeScalesFactory.getGMST(IERSConventions.IERS_2010, false)),
542                             file.getMetadata().getFrameEpoch());
543         try {
544             file.getMetadata().getLaunchYear();
545             Assertions.fail("an exception should have been thrown");
546         } catch (OrekitException oe) {
547             Assertions.assertEquals(OrekitMessages.NOT_VALID_INTERNATIONAL_DESIGNATOR, oe.getSpecifier());
548             Assertions.assertEquals("REDACTED FOR TEST PURPOSES", oe.getParts()[0]);
549         }
550         try {
551             file.getMetadata().getLaunchNumber();
552             Assertions.fail("an exception should have been thrown");
553         } catch (OrekitException oe) {
554             Assertions.assertEquals(OrekitMessages.NOT_VALID_INTERNATIONAL_DESIGNATOR, oe.getSpecifier());
555             Assertions.assertEquals("REDACTED FOR TEST PURPOSES", oe.getParts()[0]);
556         }
557         try {
558             file.getMetadata().getLaunchPiece();
559             Assertions.fail("an exception should have been thrown");
560         } catch (OrekitException oe) {
561             Assertions.assertEquals(OrekitMessages.NOT_VALID_INTERNATIONAL_DESIGNATOR, oe.getSpecifier());
562             Assertions.assertEquals("REDACTED FOR TEST PURPOSES", oe.getParts()[0]);
563         }
564     }
565 
566     @Test
567     public void testParseOPM4() {
568         //
569         final String ex = "/ccsds/odm/opm/OPMExample4.txt";
570         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
571         final Opm file = new ParserBuilder().
572                              withMu(Constants.EIGEN5C_EARTH_MU).
573                              withSimpleEOP(false).
574                              withDefaultMass(1000.0).
575                              buildOpmParser().
576                              parseMessage(source);
577         Assertions.assertEquals("TOD/2010 accurate EOP", file.getMetadata().getFrame().toString());
578         Assertions.assertEquals("2000-028A", file.getMetadata().getObjectID());
579         Assertions.assertEquals(new AbsoluteDate(2006, 6, 3, TimeScalesFactory.getUTC()), file.getDate());
580         final TimeStampedPVCoordinates pva = file.getPVCoordinates();
581         Assertions.assertEquals(  6655994.2, pva.getPosition().getX(), 1.0e-10);
582         Assertions.assertEquals(-40218575.1, pva.getPosition().getY(), 1.0e-10);
583         Assertions.assertEquals(   -82917.7, pva.getPosition().getZ(), 1.0e-10);
584         Assertions.assertEquals( 3115.48208, pva.getVelocity().getX(), 1.0e-10);
585         Assertions.assertEquals( 0470.42605, pva.getVelocity().getY(), 1.0e-10);
586         Assertions.assertEquals(-0001.01495, pva.getVelocity().getZ(), 1.0e-10);
587     }
588 
589     @Test
590     public void testParseOPM6() {
591         // simple test for OPM file, contains all mandatory information plus
592         // Spacecraft parameters and the position/velocity Covariance Matrix.
593         final String name = "/ccsds/odm/opm/OPMExample6.txt";
594         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
595         OpmParser parser = new ParserBuilder().buildOpmParser();
596         validate6(parser.parseMessage(source));
597     }
598 
599     @Test
600     public void testParseOPM7() {
601         final String ex = "/ccsds/odm/opm/OPMExample7.txt";
602         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
603         final Opm file = new ParserBuilder().buildOpmParser().parseMessage(source);
604         Frame frame = file.getMetadata().getFrame();
605         Assertions.assertSame(CelestialBodyFactory.getMars().getInertiallyOrientedFrame(), frame);
606     }
607 
608     @Test
609     public void testParseOPM8() {
610         final String ex = "/ccsds/odm/opm/OPMExample8.txt";
611         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
612         final Opm file = new ParserBuilder().buildOpmParser().parseMessage(source);
613         Frame frame = file.getMetadata().getFrame();
614         Assertions.assertSame(CelestialBodyFactory.getSolarSystemBarycenter().getInertiallyOrientedFrame(), frame);
615     }
616 
617     @Test
618     public void testParseNonStandardUnits() {
619         // this file is similar to OPMExample6.txt but uses non-standard units
620         // it is therefore NOT a regular CCSDS OPM, but is correctly parsed by Orekit
621         final String name = "/ccsds/odm/opm/OPM-non-standard-units.txt";
622         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
623         OpmParser parser = new ParserBuilder().
624                            withParsedUnitsBehavior(ParsedUnitsBehavior.CONVERT_COMPATIBLE).
625                            buildOpmParser();
626         validate6(parser.parseMessage(source));
627     }
628 
629     @Test
630     public void testRefuseNonStandardUnits() {
631         // this file is similar to OPMExample6.txt but uses non-standard units
632         // it is therefore NOT a regular CCSDS OPM, but is correctly parsed by Orekit
633         final String name = "/ccsds/odm/opm/OPM-non-standard-units.txt";
634         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
635         try {
636             new ParserBuilder().
637             withParsedUnitsBehavior(ParsedUnitsBehavior.STRICT_COMPLIANCE).
638             buildOpmParser().
639             parseMessage(source);
640             Assertions.fail("an exception should have been thrown");
641         } catch (OrekitException oe) {
642             Assertions.assertEquals(OrekitMessages.INCOMPATIBLE_UNITS, oe.getSpecifier());
643             Assertions.assertEquals("m",  oe.getParts()[0]);
644             Assertions.assertEquals("km", oe.getParts()[1]);
645         }
646     }
647 
648     private void validate6(final Opm file) {
649         Assertions.assertEquals(new AbsoluteDate(1998, 12, 18, 14, 28,
650                                                  new TimeOffset(15, TimeOffset.SECOND, 117200, TimeOffset.MICROSECOND),
651                                                  TimeScalesFactory.getGMST(IERSConventions.IERS_2010, false)),
652                             file.getMetadata().getFrameEpoch());
653         Assertions.assertEquals(1, file.getMetadata().getComments().size());
654         Assertions.assertEquals("GEOCENTRIC, CARTESIAN, EARTH FIXED", file.getMetadata().getComments().get(0));
655         Assertions.assertEquals("OREKIT-4D00FC96-AC64-11E9-BF71-001FD054093C", file.getHeader().getMessageId());
656 
657         Assertions.assertEquals(15951238.3495, file.generateKeplerianOrbit().getA(), 0.001);
658         Assertions.assertEquals(0.5914452565, file.generateKeplerianOrbit().getE(), 1.0e-10);
659         // Check Data Covariance matrix Block
660         CartesianCovariance covariance = file.getData().getCovarianceBlock();
661         Assertions.assertNotNull(covariance);
662         ArrayList<String> dataCovMatrixComment = new ArrayList<>();
663         dataCovMatrixComment.add("covariance comment 1");
664         dataCovMatrixComment.add("covariance comment 2");
665         Assertions.assertEquals(dataCovMatrixComment, covariance.getComments());
666         Assertions.assertEquals(FramesFactory.getTEME(), covariance.getReferenceFrame().asFrame());
667 
668         Array2DRowRealMatrix covMatrix = new Array2DRowRealMatrix(6, 6);
669         double[] column1 = {
670             333.1349476038534, 461.8927349220216,
671             -307.0007847730449, -0.3349365033922630,
672             -0.2211832501084875, -0.3041346050686871
673         };
674         double[] column2 = {
675             461.8927349220216, 678.2421679971363,
676             -422.1234189514228, -0.4686084221046758,
677             -0.2864186892102733, -0.4989496988610662
678         };
679         double[] column3 = {
680             -307.0007847730449, -422.1234189514228,
681             323.1931992380369, 0.2484949578400095,
682             0.1798098699846038, 0.3540310904497689
683         };
684         double[] column4 = {
685             -0.3349365033922630, -0.4686084221046758,
686             0.2484949578400095, 0.0004296022805587290,
687             0.0002608899201686016, 0.0001869263192954590
688         };
689         double[] column5 = {
690             -0.2211832501084875, -0.2864186892102733,
691             0.1798098699846038, 0.0002608899201686016,
692             0.0001767514756338532, 0.0001008862586240695
693         };
694         double[] column6 = {
695             -0.3041346050686871, -0.4989496988610662,
696             0.3540310904497689, 0.0001869263192954590,
697             0.0001008862586240695, 0.0006224444338635500
698         };
699         covMatrix.setColumn(0, column1);
700         covMatrix.setColumn(1, column2);
701         covMatrix.setColumn(2, column3);
702         covMatrix.setColumn(3, column4);
703         covMatrix.setColumn(4, column5);
704         covMatrix.setColumn(5, column6);
705         for (int i = 0; i < 6; i++) {
706             for (int j = 0; j < 6; j++) {
707                 Assertions.assertEquals(covMatrix.getEntry(i, j),
708                                     file.getData().getCovarianceBlock().getCovarianceMatrix().getEntry(i, j), 1e-15);
709             }
710         }
711 
712         // Check User defined Parameters Block
713         HashMap<String, String> userDefinedParameters = new HashMap<>();
714         userDefinedParameters.put("EARTH_MODEL", "WGS-84");
715         Assertions.assertEquals(userDefinedParameters, file.getData().getUserDefinedBlock().getParameters());
716 
717     }
718 
719     @Test
720     public void testCentersAndTimeScales() {
721 
722         final OpmParser parser = new ParserBuilder().withMu(Constants.EIGEN5C_EARTH_MU).withDefaultMass(1000.0).buildOpmParser();
723 
724         final String name1 = "/ccsds/odm/opm/OPM-dummy-solar-system-barycenter.txt";
725         final DataSource source1 = new DataSource(name1, () -> getClass().getResourceAsStream(name1));
726         Opm file1 = parser.parseMessage(source1);
727         Assertions.assertEquals("TDB", file1.getMetadata().getTimeSystem().name());
728         Assertions.assertEquals("solar system barycenter", file1.getMetadata().getCenter().getBody().getName());
729 
730         final String name2 = "/ccsds/odm/opm/OPM-dummy-ssb.txt";
731         final DataSource source2 = new DataSource(name2, () -> getClass().getResourceAsStream(name2));
732         Opm file2 = parser.parseMessage(source2);
733         Assertions.assertEquals("TCB", file2.getMetadata().getTimeSystem().name());
734         Assertions.assertEquals("solar system barycenter", file2.getMetadata().getCenter().getBody().getName());
735 
736         final String name3 = "/ccsds/odm/opm/OPM-dummy-earth-barycenter.txt";
737         final DataSource source3 = new DataSource(name3, () -> getClass().getResourceAsStream(name3));
738         Opm file3 = parser.parseMessage(source3);
739         Assertions.assertEquals("TDB", file3.getMetadata().getTimeSystem().name());
740         Assertions.assertEquals("Earth-Moon barycenter", file3.getMetadata().getCenter().getBody().getName());
741 
742         final String name4 = "/ccsds/odm/opm/OPM-dummy-earth-dash-moon-barycenter.txt";
743         final DataSource source4 = new DataSource(name4, () -> getClass().getResourceAsStream(name4));
744         Opm file4 = parser.parseMessage(source4);
745         Assertions.assertEquals("TDB", file4.getMetadata().getTimeSystem().name());
746         Assertions.assertEquals("Earth-Moon barycenter", file4.getMetadata().getCenter().getBody().getName());
747 
748         final String name5 = "/ccsds/odm/opm/OPM-dummy-earth-moon-barycenter.txt";
749         final DataSource source5 = new DataSource(name5, () -> getClass().getResourceAsStream(name5));
750         Opm file5 = parser.parseMessage(source5);
751         Assertions.assertEquals("UT1", file5.getMetadata().getTimeSystem().name());
752         Assertions.assertEquals("Earth-Moon barycenter", file5.getMetadata().getCenter().getBody().getName());
753 
754         final String name6 = "/ccsds/odm/opm/OPM-dummy-emb.txt";
755         final DataSource source6 = new DataSource(name6, () -> getClass().getResourceAsStream(name6));
756         Opm file6 = parser.parseMessage(source6);
757         Assertions.assertEquals("TT", file6.getMetadata().getTimeSystem().name());
758         Assertions.assertEquals("Earth-Moon barycenter", file6.getMetadata().getCenter().getBody().getName());
759 
760     }
761 
762     @Test
763     public void testOrbitFileInterface() {
764         final String ex = "/ccsds/odm/opm/OPMExample4.txt";
765         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
766 
767         final OpmParser parser = new ParserBuilder().withMu(Constants.EIGEN5C_EARTH_MU).withDefaultMass(1000.0).buildOpmParser();
768 
769         final Opm file = parser.parseMessage(source);
770 
771         final String satId = "2000-028A";
772         Assertions.assertEquals(satId, file.getMetadata().getObjectID());
773 
774         checkPVEntry(file.getPVCoordinates(), file.getPVCoordinates());
775 
776     }
777 
778     private void checkPVEntry(final PVCoordinates expected,
779                               final PVCoordinates actual) {
780         final Vector3D expectedPos = expected.getPosition();
781         final Vector3D expectedVel = expected.getVelocity();
782 
783         final Vector3D actualPos = actual.getPosition();
784         final Vector3D actualVel = actual.getVelocity();
785 
786         final double eps = 1e-12;
787 
788         Assertions.assertEquals(expectedPos.getX(), actualPos.getX(), eps);
789         Assertions.assertEquals(expectedPos.getY(), actualPos.getY(), eps);
790         Assertions.assertEquals(expectedPos.getZ(), actualPos.getZ(), eps);
791 
792         Assertions.assertEquals(expectedVel.getX(), actualVel.getX(), eps);
793         Assertions.assertEquals(expectedVel.getY(), actualVel.getY(), eps);
794         Assertions.assertEquals(expectedVel.getZ(), actualVel.getZ(), eps);
795     }
796 
797     @Test
798     public void testWrongODMType() {
799         final String name = "/ccsds/odm/omm/OMMExample1.txt";
800         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
801         try {
802             new ParserBuilder().
803             withMu(Constants.EIGEN5C_EARTH_MU).
804             withDefaultMass(1000.0).
805             buildOpmParser().
806             parseMessage(source);
807             Assertions.fail("an exception should have been thrown");
808         } catch (OrekitException oe) {
809             Assertions.assertEquals(OrekitMessages.UNSUPPORTED_FILE_FORMAT, oe.getSpecifier());
810             Assertions.assertEquals(name, oe.getParts()[0]);
811         }
812     }
813 
814     @Test
815     public void testNumberFormatErrorType() {
816         final String name = "/ccsds/odm/opm/OPM-number-format-error.txt";
817         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
818         try {
819             new ParserBuilder().
820             withMu(Constants.EIGEN5C_EARTH_MU).
821             withMissionReferenceDate(new AbsoluteDate()).
822             withDefaultMass(1000.0).
823             buildOpmParser().
824             parseMessage(source);
825             Assertions.fail("an exception should have been thrown");
826         } catch (OrekitException oe) {
827             Assertions.assertEquals(OrekitMessages.UNABLE_TO_PARSE_ELEMENT_IN_FILE, oe.getSpecifier());
828             Assertions.assertEquals("SEMI_MAJOR_AXIS", oe.getParts()[0]);
829             Assertions.assertEquals(17, oe.getParts()[1]);
830             Assertions.assertEquals(name, oe.getParts()[2]);
831         }
832     }
833 
834     @Test
835     public void testUnknownCenter() {
836         final String name = "/ccsds/odm/opm/OPM-unknown-center.txt";
837         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
838         final Opm opm = new ParserBuilder().
839                             withMu(Constants.EIGEN5C_EARTH_MU).
840                             withDefaultMass(1000.0).
841                             buildOpmParser().
842                             parseMessage(source);
843         Assertions.assertEquals("UNKNOWN-CENTER", opm.getMetadata().getCenter().getName());
844         Assertions.assertNull(opm.getMetadata().getCenter().getBody());
845         try {
846             opm.getMetadata().getFrame();
847             Assertions.fail("an exception should have been thrown");
848         } catch (OrekitException oe) {
849             Assertions.assertEquals(OrekitMessages.NO_DATA_LOADED_FOR_CELESTIAL_BODY, oe.getSpecifier());
850             Assertions.assertEquals("UNKNOWN-CENTER", oe.getParts()[0]);
851         }
852     }
853 
854     @Test
855     public void testUnknownFrame() {
856         final String name = "/ccsds/odm/opm/OPM-unknown-frame.txt";
857         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
858         final Opm opm = new ParserBuilder().
859                             withMu(Constants.EIGEN5C_EARTH_MU).
860                             withDefaultMass(1000.0).
861                             buildOpmParser().
862                             parseMessage(source);
863         Assertions.assertEquals("ZZRF", opm.getMetadata().getReferenceFrame().getName());
864         try {
865             opm.getMetadata().getFrame();
866             Assertions.fail("an exception should have been thrown");
867         } catch (OrekitException oe) {
868             Assertions.assertEquals(OrekitMessages.CCSDS_INVALID_FRAME, oe.getSpecifier());
869             Assertions.assertEquals("ZZRF", oe.getParts()[0]);
870         }
871     }
872 
873     @Test
874     public void testNonExistentFile() throws URISyntaxException {
875         final String realName = getClass().getResource("/ccsds/odm/opm/OPMExample1.txt").toURI().getPath();
876         final String wrongName = realName + "xxxxx";
877         final DataSource source = new DataSource(wrongName, () -> getClass().getResourceAsStream(wrongName));
878         try {
879             new ParserBuilder().withMu(Constants.EIGEN5C_EARTH_MU).withDefaultMass(1000.0).buildOpmParser().
880             parseMessage(source);
881             Assertions.fail("an exception should have been thrown");
882         } catch (OrekitException oe) {
883             Assertions.assertEquals(OrekitMessages.UNABLE_TO_FIND_FILE, oe.getSpecifier());
884             Assertions.assertEquals(wrongName, oe.getParts()[0]);
885         }
886     }
887 
888     @Test
889     public void testWrongKeyword() {
890         // simple test for OMM file, contains p/v entries and other mandatory data.
891         final String name = "/ccsds/odm/opm/OPM-wrong-keyword.txt";
892         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
893         try {
894             new ParserBuilder().
895             withMu(Constants.EIGEN5C_EARTH_MU).
896             withDefaultMass(1000.0).
897             buildOpmParser().
898             parseMessage(source);
899             Assertions.fail("an exception should have been thrown");
900         } catch (OrekitException oe) {
901             Assertions.assertEquals(OrekitMessages.CCSDS_UNEXPECTED_KEYWORD, oe.getSpecifier());
902             Assertions.assertEquals(11, ((Integer) oe.getParts()[0]).intValue());
903             Assertions.assertTrue(((String) oe.getParts()[2]).startsWith("WRONG_KEYWORD"));
904         }
905     }
906 
907     @Test
908     public void testSpuriousMetaDataSection() {
909         final String name = "/ccsds/odm/opm/spurious-metadata.xml";
910         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
911         try {
912             new ParserBuilder().
913             withMu(CelestialBodyFactory.getMars().getGM()).
914             buildOpmParser().
915             parseMessage(source);
916             Assertions.fail("an exception should have been thrown");
917         } catch (OrekitException oe) {
918             Assertions.assertEquals(OrekitMessages.CCSDS_UNEXPECTED_KEYWORD, oe.getSpecifier());
919             Assertions.assertEquals(23, ((Integer) oe.getParts()[0]).intValue());
920             Assertions.assertEquals("metadata", oe.getParts()[2]);
921         }
922     }
923 
924     @Test
925     public void testIncompatibleUnits1() {
926         final String name = "/ccsds/odm/opm/OPM-incompatible-units.txt";
927         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
928         try {
929             new ParserBuilder().
930             withParsedUnitsBehavior(ParsedUnitsBehavior.CONVERT_COMPATIBLE).
931             buildOpmParser().
932             parseMessage(source);
933             Assertions.fail("an exception should have been thrown");
934         } catch (OrekitException oe) {
935             Assertions.assertEquals(OrekitMessages.INCOMPATIBLE_UNITS, oe.getSpecifier());
936             Assertions.assertEquals("s",  oe.getParts()[0]);
937             Assertions.assertEquals("m²", oe.getParts()[1]);
938         }
939     }
940 
941     @Test
942     public void testIncompatibleUnits2() {
943         final String name = "/ccsds/odm/opm/OPM-incompatible-units.txt";
944         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
945         try {
946             new ParserBuilder().
947             withParsedUnitsBehavior(ParsedUnitsBehavior.STRICT_COMPLIANCE).
948             buildOpmParser().
949             parseMessage(source);
950             Assertions.fail("an exception should have been thrown");
951         } catch (OrekitException oe) {
952             Assertions.assertEquals(OrekitMessages.INCOMPATIBLE_UNITS, oe.getSpecifier());
953             Assertions.assertEquals("s",  oe.getParts()[0]);
954             Assertions.assertEquals("m²", oe.getParts()[1]);
955         }
956     }
957 
958     @Test
959     public void testIgnoredIncompatibleUnits() {
960         final String name = "/ccsds/odm/opm/OPM-incompatible-units.txt";
961         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
962         final Opm file = new ParserBuilder().
963                              withParsedUnitsBehavior(ParsedUnitsBehavior.IGNORE_PARSED).
964                              buildOpmParser().
965                              parseMessage(source);
966         Assertions.assertEquals(18.77, file.getData().getSpacecraftParametersBlock().getSolarRadArea(), 1.0e-10);
967     }
968 
969     @Test
970     public void testIssue619() {
971         // test for issue 619 - moon centered transformation
972         // Verify that moon is at the center of the new frame
973         CelestialBody moon = CelestialBodyFactory.getMoon();
974         AbsoluteDate date = new AbsoluteDate(2000, 1, 1, 12, 0, 0, TimeScalesFactory.getUTC());
975         final String ex = "/ccsds/odm/opm/OPM-dummy-moon-EME2000.txt";
976         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
977         final OpmParser parser = new ParserBuilder().
978                                  withMu(CelestialBodyFactory.getEarth().getGM()).
979                                  withDefaultMass(1000.0).
980                                  buildOpmParser();
981         final Opm file = parser.parseMessage(source);
982         final Frame actualFrame = file.getMetadata().getFrame();
983         MatcherAssert.assertThat(moon.getPVCoordinates(date, actualFrame),
984                                  OrekitMatchers.pvCloseTo(PVCoordinates.ZERO, 1e-3));
985     }
986 
987 }