1   /* Copyright 2002-2022 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 testIssue906() throws URISyntaxException {
158         String name = "/ccsds/odm/omm/OMM-with-units.xml";
159         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
160         final OmmParser parser = new ParserBuilder().withMu(Constants.EIGEN5C_EARTH_MU).buildOmmParser();
161 
162         validateOMM2(parser.parseMessage(source));
163     }
164 
165     @Test
166     public void testWriteOMM3() throws URISyntaxException, IOException {
167         final String name = "/ccsds/odm/omm/OMMExample2.xml";
168         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
169         OmmParser parser = new ParserBuilder().withMu(Constants.EIGEN5C_EARTH_MU).buildOmmParser();
170         final Omm original = parser.parseMessage(source);
171 
172         // write the parsed file back to a characters array
173         final CharArrayWriter caw = new CharArrayWriter();
174         final Generator generator = new KvnGenerator(caw, OmmWriter.KVN_PADDING_WIDTH, "dummy", 60);
175         new WriterBuilder().buildOmmWriter().writeMessage(generator, original);
176 
177         // reparse the written file
178         final byte[]     bytes = caw.toString().getBytes(StandardCharsets.UTF_8);
179         final DataSource source2 = new DataSource(name, () -> new ByteArrayInputStream(bytes));
180         final Omm    rebuilt = new ParserBuilder().buildOmmParser().parseMessage(source2);
181         validateOMM2(rebuilt);
182 
183     }
184 
185     private void validateOMM2(final Omm file) throws URISyntaxException {
186         Assert.assertEquals(3.0, file.getHeader().getFormatVersion(), 1.0e-10);
187         Assert.assertEquals("SGP/SGP4", file.getMetadata().getMeanElementTheory());
188         final KeplerianElements kep = file.getData().getKeplerianElementsBlock();
189         Assert.assertEquals(1.00273272, Constants.JULIAN_DAY * kep.getMeanMotion() / MathUtils.TWO_PI, 1e-10);
190         Assert.assertTrue(Double.isNaN(file.getData().getMass()));
191         CartesianCovariance covariance = file.getData().getCovarianceBlock();
192         Assert.assertEquals(FramesFactory.getTEME(), covariance.getReferenceFrame().asFrame());
193         Assert.assertEquals(6, covariance.getCovarianceMatrix().getRowDimension());
194         Assert.assertEquals(6, covariance.getCovarianceMatrix().getColumnDimension());
195         Assert.assertEquals(1995, file.getMetadata().getLaunchYear());
196         Assert.assertEquals(25, file.getMetadata().getLaunchNumber());
197         Assert.assertEquals("A", file.getMetadata().getLaunchPiece());
198         file.generateKeplerianOrbit();
199 
200         Array2DRowRealMatrix covMatrix = new Array2DRowRealMatrix(6, 6);
201         double[] column1 = {
202             333.1349476038534, 461.8927349220216,
203             -307.0007847730449, -0.3349365033922630,
204             -0.2211832501084875, -0.3041346050686871
205         };
206         double[] column2 = {
207             461.8927349220216, 678.2421679971363,
208             -422.1234189514228, -0.4686084221046758,
209             -0.2864186892102733, -0.4989496988610662
210         };
211         double[] column3 = {
212             -307.0007847730449, -422.1234189514228,
213             323.1931992380369, 0.2484949578400095,
214             0.1798098699846038, 0.3540310904497689
215         };
216         double[] column4 = {
217             -0.3349365033922630, -0.4686084221046758,
218             0.2484949578400095, 0.0004296022805587290,
219             0.0002608899201686016, 0.0001869263192954590
220         };
221         double[] column5 = {
222             -0.2211832501084875, -0.2864186892102733,
223             0.1798098699846038, 0.0002608899201686016,
224             0.0001767514756338532, 0.0001008862586240695
225         };
226         double[] column6 = {
227             -0.3041346050686871, -0.4989496988610662,
228             0.3540310904497689, 0.0001869263192954590,
229             0.0001008862586240695, 0.0006224444338635500
230         };
231         covMatrix.setColumn(0, column1);
232         covMatrix.setColumn(1, column2);
233         covMatrix.setColumn(2, column3);
234         covMatrix.setColumn(3, column4);
235         covMatrix.setColumn(4, column5);
236         covMatrix.setColumn(5, column6);
237         for (int i = 0; i < 6; i++) {
238             for (int j = 0; j < 6; j++) {
239                 Assert.assertEquals(covMatrix.getEntry(i, j),
240                                     covariance.getCovarianceMatrix().getEntry(i, j),
241                                     1e-15);
242             }
243         }
244 
245     }
246 
247     @Test
248     public void testParseOMM3() throws URISyntaxException {
249         // simple test for OMM file, contains p/v entries and other mandatory
250         // data.
251         final String name = "/ccsds/odm/omm/OMMExample3.txt";
252         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
253         final AbsoluteDate missionReferenceDate = new AbsoluteDate(2000, 1, 1, DataContext.getDefault().getTimeScales().getUTC());
254         final OmmParser parser = new ParserBuilder().
255                                  withMu(Constants.EIGEN5C_EARTH_MU).
256                                  withMissionReferenceDate(missionReferenceDate).
257                                  withDefaultMass(1000.0).
258                                  buildOmmParser();
259 
260         final Omm file = parser.parseMessage(source);
261         final KeplerianElements kep = file.getData().getKeplerianElementsBlock();
262         Assert.assertEquals(2.0, file.getHeader().getFormatVersion(), 1.0e-10);
263         Assert.assertEquals(missionReferenceDate.shiftedBy(210840), file.getMetadata().getFrameEpoch());
264         Assert.assertEquals(6800e3, kep.getA(), 1e-10);
265 
266         final SpacecraftParameters sp = file.getData().getSpacecraftParametersBlock();
267         Assert.assertEquals(300, sp.getMass(), 1e-10);
268         Assert.assertEquals(5, sp.getSolarRadArea(), 1e-10);
269         Assert.assertEquals(0.001, sp.getSolarRadCoeff(), 1e-10);
270 
271         CartesianCovariance covariance = file.getData().getCovarianceBlock();
272         Assert.assertEquals(null, covariance.getReferenceFrame().asFrame());
273         Assert.assertEquals(null, covariance.getReferenceFrame().asCelestialBodyFrame());
274         Assert.assertEquals(LOFType.TNW, covariance.getReferenceFrame().asOrbitRelativeFrame().getLofType());
275 
276         UserDefined ud = file.getData().getUserDefinedBlock();
277         HashMap<String, String> userDefinedParameters = new HashMap<String, String>();
278         userDefinedParameters.put("EARTH_MODEL", "WGS-84");
279         Assert.assertEquals(userDefinedParameters, ud.getParameters());
280         Assert.assertEquals(Arrays.asList("this is a comment", "here is another one"),
281                             file.getHeader().getComments());
282         Assert.assertEquals(Collections.singletonList("this comment doesn't say much"),
283                             file.getMetadata().getComments());
284         Assert.assertEquals(Collections.singletonList("the following data is what we're looking for"),
285                             file.getData().getKeplerianElementsBlock().getComments());
286         Assert.assertEquals(Collections.singletonList("spacecraft data"),
287                             file.getData().getSpacecraftParametersBlock().getComments());
288         Assert.assertEquals(Collections.singletonList("Covariance matrix"),
289                             file.getData().getCovarianceBlock().getComments());
290         Assert.assertEquals(1995, file.getMetadata().getLaunchYear());
291         Assert.assertEquals(25, file.getMetadata().getLaunchNumber());
292         Assert.assertEquals("A", file.getMetadata().getLaunchPiece());
293         file.generateSpacecraftState();
294         file.generateKeplerianOrbit();
295 
296     }
297 
298     @Test
299     public void testWrongKeyword() throws URISyntaxException {
300         // simple test for OMM file, contains p/v entries and other mandatory
301         // data.
302         final String name = "/ccsds/odm/omm/OMM-wrong-keyword.txt";
303         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
304         final OmmParser parser = new ParserBuilder().
305                                  withMu(Constants.EIGEN5C_EARTH_MU).
306                                  withMissionReferenceDate(new AbsoluteDate()).
307                                  withDefaultMass(1000.0).
308                                  buildOmmParser();
309         try {
310             parser.parseMessage(source);
311             Assert.fail("an exception should have been thrown");
312         } catch (OrekitException oe) {
313             Assert.assertEquals(OrekitMessages.CCSDS_UNEXPECTED_KEYWORD, oe.getSpecifier());
314             Assert.assertEquals(9, ((Integer) oe.getParts()[0]).intValue());
315             Assert.assertTrue(((String) oe.getParts()[2]).startsWith("WRONG_KEYWORD"));
316         }
317     }
318 
319     @Test
320     public void testOrbitFileInterface() {
321         // simple test for OMM file, contains p/v entries and other mandatory data.
322         final String name = "/ccsds/odm/omm/OMMExample1.txt";
323         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
324 
325         // initialize parser
326         final OmmParser parser = new ParserBuilder().
327                         withMu(398600e9).
328                         withMissionReferenceDate(new AbsoluteDate()).
329                         withDefaultMass(1000.0).
330                         buildOmmParser();
331 
332         final Omm file = parser.parseMessage(source);
333 
334         final String satId = "1995-025A";
335         Assert.assertEquals(satId, file.getMetadata().getObjectID());
336 
337     }
338 
339     @Test
340     public void testWrongODMType() {
341         final String name = "/ccsds/odm/oem/OEMExample1.txt";
342         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
343         try {
344             new ParserBuilder().
345             withConventions(IERSConventions.IERS_1996).
346             withMu(Constants.EIGEN5C_EARTH_MU).
347             withMissionReferenceDate(new AbsoluteDate()).
348             withDefaultMass(1000.0).
349             buildOmmParser().
350             parseMessage(source);
351         } catch (OrekitException oe) {
352             Assert.assertEquals(OrekitMessages.UNSUPPORTED_FILE_FORMAT, oe.getSpecifier());
353             Assert.assertEquals(name, oe.getParts()[0]);
354         }
355     }
356 
357     @Test
358     public void testNumberFormatErrorType() {
359         final String name = "/ccsds/odm/omm/OMM-number-format-error.txt";
360         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
361         try {
362             new ParserBuilder().
363             withConventions(IERSConventions.IERS_1996).
364             withMu(Constants.EIGEN5C_EARTH_MU).
365             withMissionReferenceDate(new AbsoluteDate()).
366             withDefaultMass(1000.0).
367             buildOmmParser().
368             parseMessage(source);
369         } catch (OrekitException oe) {
370             Assert.assertEquals(OrekitMessages.UNABLE_TO_PARSE_ELEMENT_IN_FILE, oe.getSpecifier());
371             Assert.assertEquals("ARG_OF_PERICENTER", oe.getParts()[0]);
372             Assert.assertEquals(15, oe.getParts()[1]);
373             Assert.assertEquals(name, oe.getParts()[2]);
374         }
375     }
376 
377     @Test
378     public void testNonExistentFile() throws URISyntaxException {
379         final String realName = "/ccsds/odm/omm/OMMExample1.txt";
380         final String wrongName = realName + "xxxxx";
381         final DataSource source = new DataSource(wrongName, () -> getClass().getResourceAsStream(wrongName));
382         try {
383             new ParserBuilder().
384             withConventions(IERSConventions.IERS_1996).
385             withMu(Constants.EIGEN5C_EARTH_MU).
386             withMissionReferenceDate(new AbsoluteDate()).
387             withDefaultMass(1000.0).
388             buildOmmParser().
389             parseMessage(source);
390             Assert.fail("an exception should have been thrown");
391         } catch (OrekitException oe) {
392             Assert.assertEquals(OrekitMessages.UNABLE_TO_FIND_FILE, oe.getSpecifier());
393             Assert.assertEquals(wrongName, oe.getParts()[0]);
394         }
395     }
396 
397 }