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.omm;
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.Arrays;
25  import java.util.Collections;
26  import java.util.HashMap;
27  
28  import org.hipparchus.linear.Array2DRowRealMatrix;
29  import org.hipparchus.util.FastMath;
30  import org.hipparchus.util.MathUtils;
31  import org.junit.jupiter.api.Assertions;
32  import org.junit.jupiter.api.BeforeEach;
33  import org.junit.jupiter.api.Test;
34  import org.orekit.Utils;
35  import org.orekit.bodies.CelestialBodyFactory;
36  import org.orekit.data.DataContext;
37  import org.orekit.data.DataSource;
38  import org.orekit.errors.OrekitException;
39  import org.orekit.errors.OrekitMessages;
40  import org.orekit.files.ccsds.ndm.ParserBuilder;
41  import org.orekit.files.ccsds.ndm.WriterBuilder;
42  import org.orekit.files.ccsds.ndm.odm.CartesianCovariance;
43  import org.orekit.files.ccsds.ndm.odm.KeplerianElements;
44  import org.orekit.files.ccsds.ndm.odm.SpacecraftParameters;
45  import org.orekit.files.ccsds.ndm.odm.UserDefined;
46  import org.orekit.files.ccsds.utils.generation.Generator;
47  import org.orekit.files.ccsds.utils.generation.KvnGenerator;
48  import org.orekit.files.ccsds.utils.lexical.ParseToken;
49  import org.orekit.files.ccsds.utils.lexical.TokenType;
50  import org.orekit.frames.FramesFactory;
51  import org.orekit.frames.LOFType;
52  import org.orekit.propagation.analytical.tle.TLE;
53  import org.orekit.time.AbsoluteDate;
54  import org.orekit.time.TimeOffset;
55  import org.orekit.time.TimeScalesFactory;
56  import org.orekit.utils.Constants;
57  import org.orekit.utils.IERSConventions;
58  
59  public class OmmParserTest {
60  
61      @BeforeEach
62      public void setUp()
63          throws Exception {
64          Utils.setDataRoot("regular-data");
65      }
66  
67      @Test
68      public void testParseOMM1() {
69          // simple test for OMM file, contains p/v entries and other mandatory
70          // data.
71          final String ex = "/ccsds/odm/omm/OMMExample1.txt";
72          final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
73  
74          // initialize parser
75          final OmmParser parser = new ParserBuilder().withMu(398600e9).withDefaultMass(1000.0).buildOmmParser();
76          final Omm   file   = parser.parseMessage(source);
77  
78          // Check Header Block;
79          Assertions.assertEquals(3.0, file.getHeader().getFormatVersion(), 1.0e-10);
80          Assertions.assertEquals(new AbsoluteDate(2007, 3, 6, 16, 0, 0,
81                                               TimeScalesFactory.getUTC()),
82                                               file.getHeader().getCreationDate());
83          Assertions.assertEquals("NOAA/USA", file.getHeader().getOriginator());
84          Assertions.assertNull(file.getHeader().getMessageId());
85  
86          // Check Metadata Block;
87  
88          Assertions.assertEquals("GOES 9", file.getMetadata().getObjectName());
89          Assertions.assertEquals("1995-025A", file.getMetadata().getObjectID());
90          Assertions.assertEquals("EARTH", file.getMetadata().getCenter().getName());
91          Assertions.assertNotNull(file.getMetadata().getCenter().getBody());
92          Assertions.assertEquals(CelestialBodyFactory.getEarth(), file.getMetadata().getCenter().getBody());
93          Assertions.assertEquals(FramesFactory.getTEME(), file.getMetadata().getFrame());
94          Assertions.assertEquals("UTC",      file.getMetadata().getTimeSystem().name());
95          Assertions.assertEquals("SGP/SGP4", file.getMetadata().getMeanElementTheory());
96          Assertions.assertEquals("TEME", file.getMetadata().getFrame().toString());
97          Assertions.assertTrue(file.getData().getTLEBlock().getComments().isEmpty());
98  
99          // Check Mean Keplerian elements data block;
100         KeplerianElements kep = file.getData().getKeplerianElementsBlock();
101         Assertions.assertEquals(new AbsoluteDate(2007, 3, 5, 10, 34, new TimeOffset(41L, 426400000000000000L),
102                                              TimeScalesFactory.getUTC()),
103                             file.getDate());
104         Assertions.assertEquals(1.00273272 * FastMath.PI / 43200.0, kep.getMeanMotion(), 1e-10);
105         Assertions.assertEquals(0.0005013, kep.getE(), 1e-10);
106         Assertions.assertEquals(FastMath.toRadians(3.0539), kep.getI(), 1e-10);
107         Assertions.assertEquals(FastMath.toRadians(81.7939), kep.getRaan(), 1e-10);
108         Assertions.assertEquals(FastMath.toRadians(249.2363), kep.getPa(), 1e-10);
109         Assertions.assertEquals(FastMath.toRadians(150.1602), kep.getAnomaly(), 1e-10);
110         Assertions.assertEquals(398600.8 * 1e9, kep.getMu(), 1e-10);
111 
112 
113         // Check TLE Related Parameters data block;
114         OmmTle tle = file.getData().getTLEBlock();
115         Assertions.assertEquals(0, tle.getEphemerisType());
116         Assertions.assertEquals('U', tle.getClassificationType());
117         int[] noradIDExpected = new int[23581];
118         int[] noradIDActual = new int[tle.getNoradID()];
119         Assertions.assertEquals(noradIDExpected[0], noradIDActual[0]);
120         Assertions.assertEquals(925, tle.getElementSetNumber());
121         int[] revAtEpochExpected = new int[4316];
122         int[] revAtEpochActual = new int[tle.getRevAtEpoch()];
123         Assertions.assertEquals(revAtEpochExpected[0], revAtEpochActual[0]);
124         Assertions.assertEquals(0.0001, tle.getBStar(), 1e-10);
125         Assertions.assertEquals(-0.00000113 * FastMath.PI / 1.86624e9, tle.getMeanMotionDot(), 1e-12);
126         Assertions.assertEquals(0.0 * FastMath.PI / 5.3747712e13, tle.getMeanMotionDotDot(), 1e-10);
127         Assertions.assertEquals(1995, file.getMetadata().getLaunchYear());
128         Assertions.assertEquals(25, file.getMetadata().getLaunchNumber());
129         Assertions.assertEquals("A", file.getMetadata().getLaunchPiece());
130         file.generateKeplerianOrbit();
131         try {
132             file.generateSpacecraftState();
133         } catch (OrekitException orekitException) {
134             Assertions.assertEquals(OrekitMessages.CCSDS_UNKNOWN_SPACECRAFT_MASS, orekitException.getSpecifier());
135         }
136         TLE generated = file.generateTLE();
137         Assertions.assertEquals("1 23581U 95025A   07064.44075725 -.00000056  00000-0  10000-3 0  9256", generated.getLine1());
138         Assertions.assertEquals("2 23581   3.0539  81.7939 0005013 249.2363 150.1602  1.00273272 43169", generated.getLine2());
139     }
140 
141     @Test
142     public void testParseOMM2KVN() throws URISyntaxException {
143         String name = "/ccsds/odm/omm/OMMExample2.txt";
144         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
145         final OmmParser parser = new ParserBuilder().withMu(Constants.EIGEN5C_EARTH_MU).buildOmmParser();
146 
147         validateOMM2(parser.parseMessage(source));
148     }
149 
150     @Test
151     public void testParseOMM2XML() throws URISyntaxException {
152         String name = "/ccsds/odm/omm/OMMExample2.xml";
153         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
154         final OmmParser parser = new ParserBuilder().withMu(Constants.EIGEN5C_EARTH_MU).buildOmmParser();
155 
156         validateOMM2(parser.parseMessage(source));
157     }
158 
159     @Test
160     public void testIssue906() throws URISyntaxException {
161         String name = "/ccsds/odm/omm/OMM-with-units.xml";
162         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
163         final OmmParser parser = new ParserBuilder().withMu(Constants.EIGEN5C_EARTH_MU).buildOmmParser();
164 
165         validateOMM2(parser.parseMessage(source));
166     }
167 
168     @Test
169     public void testWriteOMM3() throws URISyntaxException, IOException {
170         final String name = "/ccsds/odm/omm/OMMExample2.xml";
171         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
172         OmmParser parser = new ParserBuilder().withMu(Constants.EIGEN5C_EARTH_MU).buildOmmParser();
173         final Omm original = parser.parseMessage(source);
174 
175         // write the parsed file back to a characters array
176         final CharArrayWriter caw = new CharArrayWriter();
177         final Generator generator = new KvnGenerator(caw, OmmWriter.KVN_PADDING_WIDTH, "dummy",
178                                                      Constants.JULIAN_DAY, 60);
179         new WriterBuilder().buildOmmWriter().writeMessage(generator, original);
180 
181         // reparse the written file
182         final byte[]     bytes = caw.toString().getBytes(StandardCharsets.UTF_8);
183         final DataSource source2 = new DataSource(name, () -> new ByteArrayInputStream(bytes));
184         final Omm    rebuilt = new ParserBuilder().buildOmmParser().parseMessage(source2);
185         validateOMM2(rebuilt);
186 
187     }
188 
189     private void validateOMM2(final Omm file) {
190         Assertions.assertEquals(3.0, file.getHeader().getFormatVersion(), 1.0e-10);
191         Assertions.assertEquals(OmmMetadata.SGP_SGP4_THEORY, file.getMetadata().getMeanElementTheory());
192         final KeplerianElements kep = file.getData().getKeplerianElementsBlock();
193         Assertions.assertEquals(1.00273272, Constants.JULIAN_DAY * kep.getMeanMotion() / MathUtils.TWO_PI, 1e-10);
194         Assertions.assertTrue(Double.isNaN(file.getData().getMass()));
195         CartesianCovariance covariance = file.getData().getCovarianceBlock();
196         Assertions.assertEquals(FramesFactory.getTEME(), covariance.getReferenceFrame().asFrame());
197         Assertions.assertEquals(6, covariance.getCovarianceMatrix().getRowDimension());
198         Assertions.assertEquals(6, covariance.getCovarianceMatrix().getColumnDimension());
199         Assertions.assertEquals(1995, file.getMetadata().getLaunchYear());
200         Assertions.assertEquals(25, file.getMetadata().getLaunchNumber());
201         Assertions.assertEquals("A", file.getMetadata().getLaunchPiece());
202         Assertions.assertEquals(0.0001, file.getData().getTLEBlock().getBStar(), 1.0e-15);
203         Assertions.assertTrue(Double.isNaN(file.getData().getTLEBlock().getBTerm()));
204         file.generateKeplerianOrbit();
205 
206         Array2DRowRealMatrix covMatrix = new Array2DRowRealMatrix(6, 6);
207         double[] column1 = {
208             333.1349476038534, 461.8927349220216,
209             -307.0007847730449, -0.3349365033922630,
210             -0.2211832501084875, -0.3041346050686871
211         };
212         double[] column2 = {
213             461.8927349220216, 678.2421679971363,
214             -422.1234189514228, -0.4686084221046758,
215             -0.2864186892102733, -0.4989496988610662
216         };
217         double[] column3 = {
218             -307.0007847730449, -422.1234189514228,
219             323.1931992380369, 0.2484949578400095,
220             0.1798098699846038, 0.3540310904497689
221         };
222         double[] column4 = {
223             -0.3349365033922630, -0.4686084221046758,
224             0.2484949578400095, 0.0004296022805587290,
225             0.0002608899201686016, 0.0001869263192954590
226         };
227         double[] column5 = {
228             -0.2211832501084875, -0.2864186892102733,
229             0.1798098699846038, 0.0002608899201686016,
230             0.0001767514756338532, 0.0001008862586240695
231         };
232         double[] column6 = {
233             -0.3041346050686871, -0.4989496988610662,
234             0.3540310904497689, 0.0001869263192954590,
235             0.0001008862586240695, 0.0006224444338635500
236         };
237         covMatrix.setColumn(0, column1);
238         covMatrix.setColumn(1, column2);
239         covMatrix.setColumn(2, column3);
240         covMatrix.setColumn(3, column4);
241         covMatrix.setColumn(4, column5);
242         covMatrix.setColumn(5, column6);
243         for (int i = 0; i < 6; i++) {
244             for (int j = 0; j < 6; j++) {
245                 Assertions.assertEquals(covMatrix.getEntry(i, j),
246                                     covariance.getCovarianceMatrix().getEntry(i, j),
247                                     1e-15);
248             }
249         }
250 
251     }
252 
253     @Test
254     public void testParseOMM3() {
255         // simple test for OMM file, contains p/v entries and other mandatory
256         // data.
257         final String name = "/ccsds/odm/omm/OMMExample3.txt";
258         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
259         final AbsoluteDate missionReferenceDate = new AbsoluteDate(2000, 1, 1, DataContext.getDefault().getTimeScales().getUTC());
260         final OmmParser parser = new ParserBuilder().
261                                  withMu(Constants.EIGEN5C_EARTH_MU).
262                                  withMissionReferenceDate(missionReferenceDate).
263                                  withDefaultMass(1000.0).
264                                  buildOmmParser();
265 
266         final Omm file = parser.parseMessage(source);
267         final KeplerianElements kep = file.getData().getKeplerianElementsBlock();
268         Assertions.assertEquals(2.0, file.getHeader().getFormatVersion(), 1.0e-10);
269         Assertions.assertEquals(missionReferenceDate.shiftedBy(210840), file.getMetadata().getFrameEpoch());
270         Assertions.assertEquals(6800e3, kep.getA(), 1e-10);
271 
272         final SpacecraftParameters sp = file.getData().getSpacecraftParametersBlock();
273         Assertions.assertEquals(300, sp.getMass(), 1e-10);
274         Assertions.assertEquals(5, sp.getSolarRadArea(), 1e-10);
275         Assertions.assertEquals(0.001, sp.getSolarRadCoeff(), 1e-10);
276 
277         CartesianCovariance covariance = file.getData().getCovarianceBlock();
278         Assertions.assertNull(covariance.getReferenceFrame().asFrame());
279         Assertions.assertNull(covariance.getReferenceFrame().asCelestialBodyFrame());
280         Assertions.assertEquals(LOFType.TNW, covariance.getReferenceFrame().asOrbitRelativeFrame().getLofType());
281 
282         UserDefined ud = file.getData().getUserDefinedBlock();
283         HashMap<String, String> userDefinedParameters = new HashMap<>();
284         userDefinedParameters.put("EARTH_MODEL", "WGS-84");
285         Assertions.assertEquals(userDefinedParameters, ud.getParameters());
286         Assertions.assertEquals(Arrays.asList("this is a comment", "here is another one"),
287                             file.getHeader().getComments());
288         Assertions.assertEquals(Collections.singletonList("this comment doesn't say much"),
289                             file.getMetadata().getComments());
290         Assertions.assertEquals(Collections.singletonList("the following data is what we're looking for"),
291                             file.getData().getKeplerianElementsBlock().getComments());
292         Assertions.assertEquals(Collections.singletonList("spacecraft data"),
293                             file.getData().getSpacecraftParametersBlock().getComments());
294         Assertions.assertEquals(Collections.singletonList("Covariance matrix"),
295                             file.getData().getCovarianceBlock().getComments());
296         Assertions.assertEquals(1995, file.getMetadata().getLaunchYear());
297         Assertions.assertEquals(25, file.getMetadata().getLaunchNumber());
298         Assertions.assertEquals("A", file.getMetadata().getLaunchPiece());
299         file.generateSpacecraftState();
300         file.generateKeplerianOrbit();
301 
302     }
303 
304     @Test
305     public void testParseOMM5() {
306         // simple test for OMM file, contains SGP4-XP elements with BTERM
307         final String name = "/ccsds/odm/omm/OMMExample5.txt";
308         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
309         final AbsoluteDate missionReferenceDate = new AbsoluteDate(2000, 1, 1, DataContext.getDefault().getTimeScales().getUTC());
310         final OmmParser parser = new ParserBuilder().
311                                  withMu(Constants.EIGEN5C_EARTH_MU).
312                                  withMissionReferenceDate(missionReferenceDate).
313                                  buildOmmParser();
314 
315         final Omm file = parser.parseMessage(source);
316         Assertions.assertEquals(3.0, file.getHeader().getFormatVersion(), 1.0e-10);
317         Assertions.assertEquals(OmmMetadata.SGP4_XP_THEORY, file.getMetadata().getMeanElementTheory());
318         final KeplerianElements kep = file.getData().getKeplerianElementsBlock();
319         Assertions.assertEquals(1.00273272, Constants.JULIAN_DAY * kep.getMeanMotion() / MathUtils.TWO_PI, 1e-10);
320         Assertions.assertTrue(Double.isNaN(file.getData().getMass()));
321         Assertions.assertTrue(Double.isNaN(file.getData().getTLEBlock().getBStar()));
322         Assertions.assertEquals(0.0015, file.getData().getTLEBlock().getBTerm(), 1.0e-15);
323     }
324 
325     @Test
326     public void testWrongKeyword() {
327         // simple test for OMM file, contains p/v entries and other mandatory
328         // data.
329         final String name = "/ccsds/odm/omm/OMM-wrong-keyword.txt";
330         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
331         final OmmParser parser = new ParserBuilder().
332                                  withMu(Constants.EIGEN5C_EARTH_MU).
333                                  withMissionReferenceDate(new AbsoluteDate()).
334                                  withDefaultMass(1000.0).
335                                  buildOmmParser();
336         try {
337             parser.parseMessage(source);
338             Assertions.fail("an exception should have been thrown");
339         } catch (OrekitException oe) {
340             Assertions.assertEquals(OrekitMessages.CCSDS_UNEXPECTED_KEYWORD, oe.getSpecifier());
341             Assertions.assertEquals(9, ((Integer) oe.getParts()[0]).intValue());
342             Assertions.assertTrue(((String) oe.getParts()[2]).startsWith("WRONG_KEYWORD"));
343         }
344     }
345 
346     @Test
347     public void testEmptyObjectID() {
348         // test with an OMM file that does not fulfills CCSDS standard and uses an empty OBJECT_ID
349         final String name = "/ccsds/odm/omm/OMM-empty-object-id.txt";
350         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
351         final OmmParser parser = new ParserBuilder().
352                                  withMu(Constants.EIGEN5C_EARTH_MU).
353                                  withMissionReferenceDate(new AbsoluteDate()).
354                                  withDefaultMass(1000.0).
355                                  buildOmmParser();
356         try {
357             parser.parseMessage(source);
358             Assertions.fail("an exception should have been thrown");
359         } catch (OrekitException oe) {
360             Assertions.assertEquals(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, oe.getSpecifier());
361             Assertions.assertEquals("OBJECT_ID", oe.getParts()[0]);
362         }
363 
364         final String replacement = "replacement-object-id";
365         final Omm omm = new ParserBuilder().
366                         withMu(Constants.EIGEN5C_EARTH_MU).
367                         withMissionReferenceDate(new AbsoluteDate()).
368                         withDefaultMass(1000.0).
369                         withFilter(token -> {
370                             if ("OBJECT_ID".equals(token.getName()) &&
371                                             (token.getRawContent() == null || token.getRawContent().isEmpty())) {
372                                 // replace null/empty entries with specified value
373                                 return Collections.singletonList(new ParseToken(token.getType(), token.getName(),
374                                                                                 replacement, token.getUnits(),
375                                                                                 token.getLineNumber(), token.getFileName()));
376                             } else {
377                                 return Collections.singletonList(token);
378                             }
379                         }).
380                         buildOmmParser().
381                         parseMessage(source);
382         // note that object id is always converted to uppercase during parsing
383         Assertions.assertEquals(replacement.toUpperCase(), omm.getMetadata().getObjectID());
384 
385     }
386 
387     @Test
388     public void testEmptyObjectIDXml() {
389         // test with an OMM file that does not fulfills CCSDS standard and uses an empty OBJECT_ID
390         String name = "/ccsds/odm/omm/OMM-empty-object-id.xml";
391         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
392         final OmmParser parser = new ParserBuilder().
393                         withMu(Constants.EIGEN5C_EARTH_MU).
394                         withMissionReferenceDate(new AbsoluteDate()).
395                         withDefaultMass(1000.0).
396                         buildOmmParser();
397         try {
398             parser.parseMessage(source);
399             Assertions.fail("an exception should have been thrown");
400         } catch (OrekitException oe) {
401             Assertions.assertEquals(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, oe.getSpecifier());
402             Assertions.assertEquals("OBJECT_ID", oe.getParts()[0]);
403         }
404 
405         final String replacement = "replacement-object-id";
406         final Omm omm = new ParserBuilder().
407                         withMu(Constants.EIGEN5C_EARTH_MU).
408                         withMissionReferenceDate(new AbsoluteDate()).
409                         withDefaultMass(1000.0).
410                         withFilter(token -> {
411                             if ("OBJECT_ID".equals(token.getName()) &&
412                                 (token.getRawContent() == null || token.getRawContent().isEmpty())) {
413                                 // replace null/empty entries with specified value
414                                 return Collections.singletonList(new ParseToken(token.getType(), token.getName(),
415                                                                                 replacement, token.getUnits(),
416                                                                                 token.getLineNumber(), token.getFileName()));
417                             } else {
418                                 return Collections.singletonList(token);
419                             }
420                         }).
421                         buildOmmParser().
422                         parseMessage(source);
423         // note that object id is always converted to uppercase during parsing
424         Assertions.assertEquals(replacement.toUpperCase(), omm.getMetadata().getObjectID());
425     }
426 
427     @Test
428     public void testRemoveUserData() {
429         final String name = "/ccsds/odm/omm/OMMExample3.txt";
430         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
431         final AbsoluteDate missionReferenceDate = new AbsoluteDate(2000, 1, 1, DataContext.getDefault().getTimeScales().getUTC());
432         final Omm omm = new ParserBuilder().
433                         withMu(Constants.EIGEN5C_EARTH_MU).
434                         withMissionReferenceDate(missionReferenceDate).
435                         withDefaultMass(1000.0).
436                         withFilter(token -> {
437                             if (token.getName().startsWith("USER_DEFINED")) {
438                                 return Collections.emptyList();
439                             } else {
440                                 return Collections.singletonList(token);
441                             }
442                         }).
443                         buildOmmParser().
444                         parseMessage(source);
445         Assertions.assertNull(omm.getData().getUserDefinedBlock());
446     }
447 
448     @Test
449     public void testChangeVersionAndAddMessageId() {
450         final String name = "/ccsds/odm/omm/OMMExample3.txt";
451         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
452         final AbsoluteDate missionReferenceDate = new AbsoluteDate(2000, 1, 1, DataContext.getDefault().getTimeScales().getUTC());
453         final String myMessageId = "custom-message-id";
454         final Omm omm = new ParserBuilder().
455                         withMu(Constants.EIGEN5C_EARTH_MU).
456                         withMissionReferenceDate(missionReferenceDate).
457                         withDefaultMass(1000.0).
458                         withFilter(token -> {
459                             if ("CCSDS_OMM_VERS".equals(token.getName())) {
460                                 // enforce ODM V3
461                                 return Collections.singletonList(new ParseToken(token.getType(), token.getName(),
462                                                                                 "3.0", token.getUnits(),
463                                                                                 token.getLineNumber(), token.getFileName()));
464                             } else {
465                                 return Collections.singletonList(token);
466                             }
467                         }).
468                         withFilter(token -> {
469                             if ("ORIGINATOR".equals(token.getName())) {
470                                 // add generated message ID after ORIGINATOR entry
471                                 return Arrays.asList(token,
472                                                      new ParseToken(TokenType.ENTRY, "MESSAGE_ID",
473                                                                     myMessageId, null,
474                                                                     -1, token.getFileName()));
475                             } else {
476                                 return Collections.singletonList(token);
477                             }
478                         }).
479                         buildOmmParser().
480                         parseMessage(source);
481         Assertions.assertEquals(3.0, omm.getHeader().getFormatVersion(), 1.0e-10);
482         Assertions.assertEquals("NOAA/USA", omm.getHeader().getOriginator());
483         Assertions.assertEquals(myMessageId, omm.getHeader().getMessageId());
484     }
485 
486     @Test
487     public void testOrbitFileInterface() {
488         // simple test for OMM file, contains p/v entries and other mandatory data.
489         final String name = "/ccsds/odm/omm/OMMExample1.txt";
490         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
491 
492         // initialize parser
493         final OmmParser parser = new ParserBuilder().
494                         withMu(398600e9).
495                         withMissionReferenceDate(new AbsoluteDate()).
496                         withDefaultMass(1000.0).
497                         buildOmmParser();
498 
499         final Omm file = parser.parseMessage(source);
500 
501         final String satId = "1995-025A";
502         Assertions.assertEquals(satId, file.getMetadata().getObjectID());
503 
504     }
505 
506     @Test
507     public void testWrongODMType() {
508         final String name = "/ccsds/odm/oem/OEMExample1.txt";
509         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
510         try {
511             new ParserBuilder().
512             withConventions(IERSConventions.IERS_1996).
513             withMu(Constants.EIGEN5C_EARTH_MU).
514             withMissionReferenceDate(new AbsoluteDate()).
515             withDefaultMass(1000.0).
516             buildOmmParser().
517             parseMessage(source);
518         } catch (OrekitException oe) {
519             Assertions.assertEquals(OrekitMessages.UNSUPPORTED_FILE_FORMAT, oe.getSpecifier());
520             Assertions.assertEquals(name, oe.getParts()[0]);
521         }
522     }
523 
524     @Test
525     public void testSpuriousMetaDataSection() {
526         final String name = "/ccsds/odm/omm/spurious-metadata.xml";
527         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
528         try {
529             new ParserBuilder().buildOmmParser().parseMessage(source);
530             Assertions.fail("an exception should have been thrown");
531         } catch (OrekitException oe) {
532             Assertions.assertEquals(OrekitMessages.CCSDS_UNEXPECTED_KEYWORD, oe.getSpecifier());
533             Assertions.assertEquals(17, ((Integer) oe.getParts()[0]).intValue());
534             Assertions.assertEquals("metadata", oe.getParts()[2]);
535         }
536     }
537 
538     @Test
539     public void testNumberFormatErrorType() {
540         final String name = "/ccsds/odm/omm/OMM-number-format-error.txt";
541         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
542         try {
543             new ParserBuilder().
544             withConventions(IERSConventions.IERS_1996).
545             withMu(Constants.EIGEN5C_EARTH_MU).
546             withMissionReferenceDate(new AbsoluteDate()).
547             withDefaultMass(1000.0).
548             buildOmmParser().
549             parseMessage(source);
550         } catch (OrekitException oe) {
551             Assertions.assertEquals(OrekitMessages.UNABLE_TO_PARSE_ELEMENT_IN_FILE, oe.getSpecifier());
552             Assertions.assertEquals("ARG_OF_PERICENTER", oe.getParts()[0]);
553             Assertions.assertEquals(15, oe.getParts()[1]);
554             Assertions.assertEquals(name, oe.getParts()[2]);
555         }
556     }
557 
558     @Test
559     public void testNonExistentFile() {
560         final String realName = "/ccsds/odm/omm/OMMExample1.txt";
561         final String wrongName = realName + "xxxxx";
562         final DataSource source = new DataSource(wrongName, () -> getClass().getResourceAsStream(wrongName));
563         try {
564             new ParserBuilder().
565             withConventions(IERSConventions.IERS_1996).
566             withMu(Constants.EIGEN5C_EARTH_MU).
567             withMissionReferenceDate(new AbsoluteDate()).
568             withDefaultMass(1000.0).
569             buildOmmParser().
570             parseMessage(source);
571             Assertions.fail("an exception should have been thrown");
572         } catch (OrekitException oe) {
573             Assertions.assertEquals(OrekitMessages.UNABLE_TO_FIND_FILE, oe.getSpecifier());
574             Assertions.assertEquals(wrongName, oe.getParts()[0]);
575         }
576     }
577 
578 }