1   /* Copyright 2002-2021 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.Assert;
32  import org.junit.Before;
33  import org.junit.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.frames.FramesFactory;
49  import org.orekit.frames.LOFType;
50  import org.orekit.propagation.analytical.tle.TLE;
51  import org.orekit.time.AbsoluteDate;
52  import org.orekit.time.TimeScalesFactory;
53  import org.orekit.utils.Constants;
54  import org.orekit.utils.IERSConventions;
55  
56  public class OmmParserTest {
57  
58      @Before
59      public void setUp()
60          throws Exception {
61          Utils.setDataRoot("regular-data");
62      }
63  
64      @Test
65      public void testParseOMM1() {
66          // simple test for OMM file, contains p/v entries and other mandatory
67          // data.
68          final String ex = "/ccsds/odm/omm/OMMExample1.txt";
69          final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
70  
71          // initialize parser
72          final OmmParser parser = new ParserBuilder().withMu(398600e9).withDefaultMass(1000.0).buildOmmParser();
73          final Omm   file   = parser.parseMessage(source);
74  
75          // Check Header Block;
76          Assert.assertEquals(3.0, file.getHeader().getFormatVersion(), 1.0e-10);
77          Assert.assertEquals(new AbsoluteDate(2007, 03, 06, 16, 00, 00,
78                                               TimeScalesFactory.getUTC()),
79                                               file.getHeader().getCreationDate());
80          Assert.assertEquals("NOAA/USA", file.getHeader().getOriginator());
81          Assert.assertNull(file.getHeader().getMessageId());
82  
83          // Check Metadata Block;
84  
85          Assert.assertEquals("GOES 9", file.getMetadata().getObjectName());
86          Assert.assertEquals("1995-025A", file.getMetadata().getObjectID());
87          Assert.assertEquals("EARTH", file.getMetadata().getCenter().getName());
88          Assert.assertNotNull(file.getMetadata().getCenter().getBody());
89          Assert.assertEquals(CelestialBodyFactory.getEarth(), file.getMetadata().getCenter().getBody());
90          Assert.assertEquals(FramesFactory.getTEME(), file.getMetadata().getFrame());
91          Assert.assertEquals("UTC",      file.getMetadata().getTimeSystem().name());
92          Assert.assertEquals("SGP/SGP4", file.getMetadata().getMeanElementTheory());
93          Assert.assertEquals("TEME", file.getMetadata().getFrame().toString());
94          Assert.assertTrue(file.getData().getTLEBlock().getComments().isEmpty());
95  
96          // Check Mean Keplerian elements data block;
97          KeplerianElements kep = file.getData().getKeplerianElementsBlock();
98          Assert.assertEquals(new AbsoluteDate(2007, 03, 05, 10, 34, 41.4264,
99                                               TimeScalesFactory.getUTC()),
100                             file.getDate());
101         Assert.assertEquals(1.00273272 * FastMath.PI / 43200.0, kep.getMeanMotion(), 1e-10);
102         Assert.assertEquals(0.0005013, kep.getE(), 1e-10);
103         Assert.assertEquals(FastMath.toRadians(3.0539), kep.getI(), 1e-10);
104         Assert.assertEquals(FastMath.toRadians(81.7939), kep.getRaan(), 1e-10);
105         Assert.assertEquals(FastMath.toRadians(249.2363), kep.getPa(), 1e-10);
106         Assert.assertEquals(FastMath.toRadians(150.1602), kep.getAnomaly(), 1e-10);
107         Assert.assertEquals(398600.8 * 1e9, kep.getMu(), 1e-10);
108 
109 
110         // Check TLE Related Parameters data block;
111         OmmTle tle = file.getData().getTLEBlock();
112         Assert.assertEquals(0, tle.getEphemerisType());
113         Assert.assertEquals('U', tle.getClassificationType());
114         int[] noradIDExpected = new int[23581];
115         int[] noradIDActual = new int[tle.getNoradID()];
116         Assert.assertEquals(noradIDExpected[0], noradIDActual[0]);
117         Assert.assertEquals(925, tle.getElementSetNumber());
118         int[] revAtEpochExpected = new int[4316];
119         int[] revAtEpochActual = new int[tle.getRevAtEpoch()];
120         Assert.assertEquals(revAtEpochExpected[0], revAtEpochActual[0]);
121         Assert.assertEquals(0.0001, tle.getBStar(), 1e-10);
122         Assert.assertEquals(-0.00000113 * FastMath.PI / 1.86624e9, tle.getMeanMotionDot(), 1e-12);
123         Assert.assertEquals(0.0 * FastMath.PI / 5.3747712e13, tle.getMeanMotionDotDot(), 1e-10);
124         Assert.assertEquals(1995, file.getMetadata().getLaunchYear());
125         Assert.assertEquals(25, file.getMetadata().getLaunchNumber());
126         Assert.assertEquals("A", file.getMetadata().getLaunchPiece());
127         file.generateKeplerianOrbit();
128         try {
129             file.generateSpacecraftState();
130         } catch (OrekitException orekitException) {
131             Assert.assertEquals(OrekitMessages.CCSDS_UNKNOWN_SPACECRAFT_MASS, orekitException.getSpecifier());
132         }
133         TLE generated = file.generateTLE();
134         Assert.assertEquals("1 23581U 95025A   07064.44075725 -.00000113  00000-0  10000-3 0  9250", generated.getLine1());
135         Assert.assertEquals("2 23581   3.0539  81.7939 0005013 249.2363 150.1602  1.00273272 43169", generated.getLine2());
136     }
137 
138     @Test
139     public void testParseOMM2KVN() throws URISyntaxException {
140         String name = "/ccsds/odm/omm/OMMExample2.txt";
141         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
142         final OmmParser parser = new ParserBuilder().withMu(Constants.EIGEN5C_EARTH_MU).buildOmmParser();
143 
144         validateOMM2(parser.parseMessage(source));
145     }
146 
147     @Test
148     public void testParseOMM2XML() throws URISyntaxException {
149         String name = "/ccsds/odm/omm/OMMExample2.xml";
150         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
151         final OmmParser parser = new ParserBuilder().withMu(Constants.EIGEN5C_EARTH_MU).buildOmmParser();
152 
153         validateOMM2(parser.parseMessage(source));
154     }
155 
156     @Test
157     public void testWriteOMM3() throws URISyntaxException, IOException {
158         final String name = "/ccsds/odm/omm/OMMExample2.xml";
159         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
160         OmmParser parser = new ParserBuilder().withMu(Constants.EIGEN5C_EARTH_MU).buildOmmParser();
161         final Omm original = parser.parseMessage(source);
162 
163         // write the parsed file back to a characters array
164         final CharArrayWriter caw = new CharArrayWriter();
165         final Generator generator = new KvnGenerator(caw, OmmWriter.KVN_PADDING_WIDTH, "dummy", 60);
166         new WriterBuilder().buildOmmWriter().writeMessage(generator, original);
167 
168         // reparse the written file
169         final byte[]     bytes = caw.toString().getBytes(StandardCharsets.UTF_8);
170         final DataSource source2 = new DataSource(name, () -> new ByteArrayInputStream(bytes));
171         final Omm    rebuilt = new ParserBuilder().buildOmmParser().parseMessage(source2);
172         validateOMM2(rebuilt);
173 
174     }
175 
176     private void validateOMM2(final Omm file) throws URISyntaxException {
177         Assert.assertEquals(3.0, file.getHeader().getFormatVersion(), 1.0e-10);
178         Assert.assertEquals("SGP/SGP4", file.getMetadata().getMeanElementTheory());
179         final KeplerianElements kep = file.getData().getKeplerianElementsBlock();
180         Assert.assertEquals(1.00273272, Constants.JULIAN_DAY * kep.getMeanMotion() / MathUtils.TWO_PI, 1e-10);
181         Assert.assertTrue(Double.isNaN(file.getData().getMass()));
182         CartesianCovariance covariance = file.getData().getCovarianceBlock();
183         Assert.assertEquals(FramesFactory.getTEME(), covariance.getReferenceFrame().asFrame());
184         Assert.assertEquals(6, covariance.getCovarianceMatrix().getRowDimension());
185         Assert.assertEquals(6, covariance.getCovarianceMatrix().getColumnDimension());
186         Assert.assertEquals(1995, file.getMetadata().getLaunchYear());
187         Assert.assertEquals(25, file.getMetadata().getLaunchNumber());
188         Assert.assertEquals("A", file.getMetadata().getLaunchPiece());
189         file.generateKeplerianOrbit();
190 
191         Array2DRowRealMatrix covMatrix = new Array2DRowRealMatrix(6, 6);
192         double[] column1 = {
193             333.1349476038534, 461.8927349220216,
194             -307.0007847730449, -0.3349365033922630,
195             -0.2211832501084875, -0.3041346050686871
196         };
197         double[] column2 = {
198             461.8927349220216, 678.2421679971363,
199             -422.1234189514228, -0.4686084221046758,
200             -0.2864186892102733, -0.4989496988610662
201         };
202         double[] column3 = {
203             -307.0007847730449, -422.1234189514228,
204             323.1931992380369, 0.2484949578400095,
205             0.1798098699846038, 0.3540310904497689
206         };
207         double[] column4 = {
208             -0.3349365033922630, -0.4686084221046758,
209             0.2484949578400095, 0.0004296022805587290,
210             0.0002608899201686016, 0.0001869263192954590
211         };
212         double[] column5 = {
213             -0.2211832501084875, -0.2864186892102733,
214             0.1798098699846038, 0.0002608899201686016,
215             0.0001767514756338532, 0.0001008862586240695
216         };
217         double[] column6 = {
218             -0.3041346050686871, -0.4989496988610662,
219             0.3540310904497689, 0.0001869263192954590,
220             0.0001008862586240695, 0.0006224444338635500
221         };
222         covMatrix.setColumn(0, column1);
223         covMatrix.setColumn(1, column2);
224         covMatrix.setColumn(2, column3);
225         covMatrix.setColumn(3, column4);
226         covMatrix.setColumn(4, column5);
227         covMatrix.setColumn(5, column6);
228         for (int i = 0; i < 6; i++) {
229             for (int j = 0; j < 6; j++) {
230                 Assert.assertEquals(covMatrix.getEntry(i, j),
231                                     covariance.getCovarianceMatrix().getEntry(i, j),
232                                     1e-15);
233             }
234         }
235 
236     }
237 
238     @Test
239     public void testParseOMM3() throws URISyntaxException {
240         // simple test for OMM file, contains p/v entries and other mandatory
241         // data.
242         final String name = "/ccsds/odm/omm/OMMExample3.txt";
243         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
244         final AbsoluteDate missionReferenceDate = new AbsoluteDate(2000, 1, 1, DataContext.getDefault().getTimeScales().getUTC());
245         final OmmParser parser = new ParserBuilder().
246                                  withMu(Constants.EIGEN5C_EARTH_MU).
247                                  withMissionReferenceDate(missionReferenceDate).
248                                  withDefaultMass(1000.0).
249                                  buildOmmParser();
250 
251         final Omm file = parser.parseMessage(source);
252         final KeplerianElements kep = file.getData().getKeplerianElementsBlock();
253         Assert.assertEquals(2.0, file.getHeader().getFormatVersion(), 1.0e-10);
254         Assert.assertEquals(missionReferenceDate.shiftedBy(210840), file.getMetadata().getFrameEpoch());
255         Assert.assertEquals(6800e3, kep.getA(), 1e-10);
256 
257         final SpacecraftParameters sp = file.getData().getSpacecraftParametersBlock();
258         Assert.assertEquals(300, sp.getMass(), 1e-10);
259         Assert.assertEquals(5, sp.getSolarRadArea(), 1e-10);
260         Assert.assertEquals(0.001, sp.getSolarRadCoeff(), 1e-10);
261 
262         CartesianCovariance covariance = file.getData().getCovarianceBlock();
263         Assert.assertEquals(null, covariance.getReferenceFrame().asFrame());
264         Assert.assertEquals(null, covariance.getReferenceFrame().asCelestialBodyFrame());
265         Assert.assertEquals(LOFType.TNW, covariance.getReferenceFrame().asOrbitRelativeFrame().getLofType());
266 
267         UserDefined ud = file.getData().getUserDefinedBlock();
268         HashMap<String, String> userDefinedParameters = new HashMap<String, String>();
269         userDefinedParameters.put("EARTH_MODEL", "WGS-84");
270         Assert.assertEquals(userDefinedParameters, ud.getParameters());
271         Assert.assertEquals(Arrays.asList("this is a comment", "here is another one"),
272                             file.getHeader().getComments());
273         Assert.assertEquals(Collections.singletonList("this comment doesn't say much"),
274                             file.getMetadata().getComments());
275         Assert.assertEquals(Collections.singletonList("the following data is what we're looking for"),
276                             file.getData().getKeplerianElementsBlock().getComments());
277         Assert.assertEquals(Collections.singletonList("spacecraft data"),
278                             file.getData().getSpacecraftParametersBlock().getComments());
279         Assert.assertEquals(Collections.singletonList("Covariance matrix"),
280                             file.getData().getCovarianceBlock().getComments());
281         Assert.assertEquals(1995, file.getMetadata().getLaunchYear());
282         Assert.assertEquals(25, file.getMetadata().getLaunchNumber());
283         Assert.assertEquals("A", file.getMetadata().getLaunchPiece());
284         file.generateSpacecraftState();
285         file.generateKeplerianOrbit();
286 
287     }
288 
289     @Test
290     public void testWrongKeyword() throws URISyntaxException {
291         // simple test for OMM file, contains p/v entries and other mandatory
292         // data.
293         final String name = "/ccsds/odm/omm/OMM-wrong-keyword.txt";
294         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
295         final OmmParser parser = new ParserBuilder().
296                                  withMu(Constants.EIGEN5C_EARTH_MU).
297                                  withMissionReferenceDate(new AbsoluteDate()).
298                                  withDefaultMass(1000.0).
299                                  buildOmmParser();
300         try {
301             parser.parseMessage(source);
302             Assert.fail("an exception should have been thrown");
303         } catch (OrekitException oe) {
304             Assert.assertEquals(OrekitMessages.CCSDS_UNEXPECTED_KEYWORD, oe.getSpecifier());
305             Assert.assertEquals(9, ((Integer) oe.getParts()[0]).intValue());
306             Assert.assertTrue(((String) oe.getParts()[2]).startsWith("WRONG_KEYWORD"));
307         }
308     }
309 
310     @Test
311     public void testOrbitFileInterface() {
312         // simple test for OMM file, contains p/v entries and other mandatory data.
313         final String name = "/ccsds/odm/omm/OMMExample1.txt";
314         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
315 
316         // initialize parser
317         final OmmParser parser = new ParserBuilder().
318                         withMu(398600e9).
319                         withMissionReferenceDate(new AbsoluteDate()).
320                         withDefaultMass(1000.0).
321                         buildOmmParser();
322 
323         final Omm file = parser.parseMessage(source);
324 
325         final String satId = "1995-025A";
326         Assert.assertEquals(satId, file.getMetadata().getObjectID());
327 
328     }
329 
330     @Test
331     public void testWrongODMType() {
332         final String name = "/ccsds/odm/oem/OEMExample1.txt";
333         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
334         try {
335             new ParserBuilder().
336             withConventions(IERSConventions.IERS_1996).
337             withMu(Constants.EIGEN5C_EARTH_MU).
338             withMissionReferenceDate(new AbsoluteDate()).
339             withDefaultMass(1000.0).
340             buildOmmParser().
341             parseMessage(source);
342         } catch (OrekitException oe) {
343             Assert.assertEquals(OrekitMessages.UNSUPPORTED_FILE_FORMAT, oe.getSpecifier());
344             Assert.assertEquals(name, oe.getParts()[0]);
345         }
346     }
347 
348     @Test
349     public void testNumberFormatErrorType() {
350         final String name = "/ccsds/odm/omm/OMM-number-format-error.txt";
351         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
352         try {
353             new ParserBuilder().
354             withConventions(IERSConventions.IERS_1996).
355             withMu(Constants.EIGEN5C_EARTH_MU).
356             withMissionReferenceDate(new AbsoluteDate()).
357             withDefaultMass(1000.0).
358             buildOmmParser().
359             parseMessage(source);
360         } catch (OrekitException oe) {
361             Assert.assertEquals(OrekitMessages.UNABLE_TO_PARSE_ELEMENT_IN_FILE, oe.getSpecifier());
362             Assert.assertEquals("ARG_OF_PERICENTER", oe.getParts()[0]);
363             Assert.assertEquals(15, oe.getParts()[1]);
364             Assert.assertEquals(name, oe.getParts()[2]);
365         }
366     }
367 
368     @Test
369     public void testNonExistentFile() throws URISyntaxException {
370         final String realName = "/ccsds/odm/omm/OMMExample1.txt";
371         final String wrongName = realName + "xxxxx";
372         final DataSource source = new DataSource(wrongName, () -> getClass().getResourceAsStream(wrongName));
373         try {
374             new ParserBuilder().
375             withConventions(IERSConventions.IERS_1996).
376             withMu(Constants.EIGEN5C_EARTH_MU).
377             withMissionReferenceDate(new AbsoluteDate()).
378             withDefaultMass(1000.0).
379             buildOmmParser().
380             parseMessage(source);
381             Assert.fail("an exception should have been thrown");
382         } catch (OrekitException oe) {
383             Assert.assertEquals(OrekitMessages.UNABLE_TO_FIND_FILE, oe.getSpecifier());
384             Assert.assertEquals(wrongName, oe.getParts()[0]);
385         }
386     }
387 
388 }