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