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;
18  
19  import org.hipparchus.random.RandomGenerator;
20  import org.hipparchus.random.Well19937a;
21  import org.junit.jupiter.api.Assertions;
22  import org.junit.jupiter.api.BeforeEach;
23  import org.junit.jupiter.api.Test;
24  import org.orekit.Utils;
25  import org.orekit.data.DataSource;
26  import org.orekit.errors.OrekitException;
27  import org.orekit.errors.OrekitMessages;
28  import org.orekit.files.ccsds.utils.generation.Generator;
29  import org.orekit.files.ccsds.utils.generation.XmlGenerator;
30  import org.orekit.time.AbsoluteDate;
31  import org.orekit.time.TimeScalesFactory;
32  import org.orekit.utils.Constants;
33  
34  import java.io.ByteArrayInputStream;
35  import java.io.CharArrayWriter;
36  import java.io.IOException;
37  import java.nio.charset.StandardCharsets;
38  import java.util.ArrayList;
39  import java.util.Arrays;
40  import java.util.List;
41  
42  /**
43   * Test class for CCSDS Navigation Data Message writing.<p>
44   * @author Luc Maisonobe
45   */
46  public class NdmWriterTest {
47  
48      @BeforeEach
49      public void setUp() {
50          Utils.setDataRoot("regular-data");
51      }
52  
53      @Test
54      public void testOpm() throws IOException {
55          final String name = "/ccsds/ndm/NDM-opm.xml";
56          final DataSource source = new DataSource(name, () -> NdmWriterTest.class.getResourceAsStream(name));
57          final Ndm ndm = new ParserBuilder().buildNdmParser().parseMessage(source);
58          final NdmWriter writer = new WriterBuilder().buildNdmWriter();
59          final CharArrayWriter caw = new CharArrayWriter();
60          try (Generator generator = new XmlGenerator(caw, XmlGenerator.DEFAULT_INDENT, "dummy.xml",
61                                                      Constants.JULIAN_DAY, true,
62                                                      XmlGenerator.NDM_XML_V3_SCHEMA_LOCATION)) {
63              writer.writeMessage(generator, ndm);
64          }
65          final byte[]      bytes  = caw.toString().getBytes(StandardCharsets.UTF_8);
66          final DataSource source2 = new DataSource(name, () -> new ByteArrayInputStream(bytes));
67          NdmTestUtils.checkContainer(ndm, new ParserBuilder().buildNdmParser().parseMessage(source2));
68          Assertions.assertTrue(caw.toString().contains("<ndm xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"https://sanaregistry.org/r/ndmxml_unqualified/ndmxml-3.0.0-master-3.0.xsd\">"));
69      }
70  
71      @Test
72      public void testOpmApm() throws IOException {
73          final String name = "/ccsds/ndm/NDM-ocm-apm.xml";
74          final DataSource source = new DataSource(name, () -> NdmWriterTest.class.getResourceAsStream(name));
75          final Ndm ndm = new ParserBuilder().buildNdmParser().parseMessage(source);
76          final NdmWriter writer = new WriterBuilder().buildNdmWriter();
77          final CharArrayWriter caw  = new CharArrayWriter();
78          try (final Generator generator = new XmlGenerator(caw, XmlGenerator.DEFAULT_INDENT, "dummy.xml",
79                                                            Constants.JULIAN_DAY, true, null)) {
80              writer.writeMessage(generator, ndm);
81          }
82          final byte[]      bytes  = caw.toString().getBytes(StandardCharsets.UTF_8);
83          final DataSource source2 = new DataSource(name, () -> new ByteArrayInputStream(bytes));
84          NdmTestUtils.checkContainer(ndm, new ParserBuilder().buildNdmParser().parseMessage(source2));
85          Assertions.assertTrue(caw.toString().contains("<ndm>"));
86      }
87  
88      @Test
89      public void testMisplacedComments() throws IOException {
90          final String name = "/ccsds/ndm/NDM-opm.xml";
91          final DataSource source = new DataSource(name, () -> NdmWriterTest.class.getResourceAsStream(name));
92          final Ndm ndm = new ParserBuilder().buildNdmParser().parseMessage(source);
93          final NdmWriter writer = new WriterBuilder().buildNdmWriter();
94          final CharArrayWriter caw  = new CharArrayWriter();
95          try (final Generator generator = new XmlGenerator(caw, XmlGenerator.DEFAULT_INDENT, "dummy.xml",
96                                                            Constants.JULIAN_DAY, true,
97                                                            XmlGenerator.NDM_XML_V3_SCHEMA_LOCATION)) {
98              for (final String comment : ndm.getComments()) {
99                  writer.writeComment(generator, comment);
100             }
101             for (final NdmConstituent<?, ?> constituent : ndm.getConstituents()) {
102                 writer.writeConstituent(generator, constituent);
103             }
104             try {
105                 writer.writeComment(generator, "we are not allowed to put comments after constituents");
106                 Assertions.fail("an exception should have been thrown");
107             } catch (OrekitException oe) {
108                 Assertions.assertEquals(OrekitMessages.ATTEMPT_TO_GENERATE_MALFORMED_FILE, oe.getSpecifier());
109                 Assertions.assertEquals("dummy.xml", oe.getParts()[0]);
110             }
111         }
112     }
113 
114     @Test
115     public void testRandomizedNdm() throws IOException {
116 
117         final ParserBuilder pb = new ParserBuilder().
118                                       withParsedUnitsBehavior(ParsedUnitsBehavior.STRICT_COMPLIANCE).
119                                       withMu(Constants.EIGEN5C_EARTH_MU).
120                                       withMissionReferenceDate(new AbsoluteDate("1996-12-17T00:00:00.000", TimeScalesFactory.getUTC()));
121 
122         final WriterBuilder wb = new WriterBuilder().
123                                  withMissionReferenceDate(pb.getMissionReferenceDate());
124 
125         // pool of constituents
126         List<NdmConstituent<?, ?>> pool = buildPool(pb);
127 
128         RandomGenerator random = new Well19937a(0x4a21ffc6d5b7dbe6l);
129         for (int i = 0; i < 100; ++i) {
130 
131             final String[] comments = new String[random.nextInt(3)];
132             for (int k = 0; k < comments.length; ++k) {
133                 comments[k] = Integer.toString(random.nextInt());
134             }
135 
136             final NdmConstituent<?, ?>[] constituents = new NdmConstituent<?,?>[1 + random.nextInt(20)];
137             for (int k = 0; k < constituents.length; ++k) {
138                 constituents[k] = pool.get(random.nextInt(pool.size()));
139             }
140 
141             // create randomized NDM
142             final Ndm original = new Ndm(Arrays.asList(comments), Arrays.asList(constituents));
143 
144             // write it
145             final CharArrayWriter caw  = new CharArrayWriter();
146             try (final Generator generator = new XmlGenerator(caw, XmlGenerator.DEFAULT_INDENT, "dummy.xml",
147                                                               Constants.JULIAN_DAY, true,
148                                                               XmlGenerator.NDM_XML_V3_SCHEMA_LOCATION)) {
149                 wb.buildNdmWriter().writeMessage(generator, original);
150             }
151 
152             // parse the written message back
153             final byte[]      bytes  = caw.toString().getBytes(StandardCharsets.UTF_8);
154             final DataSource source2 = new DataSource("dummy.xml", () -> new ByteArrayInputStream(bytes));
155             final Ndm rebuilt = pb.buildNdmParser().parseMessage(source2);
156 
157             // check we recovered the message properly
158             NdmTestUtils.checkContainer(original, rebuilt);
159 
160         }
161 
162     }
163 
164     /** build a pool of NdmConstituent.
165      * @param builder builder for constituents parsers
166      * @return pool of NdmConstituen
167      */
168     private List<NdmConstituent<?, ?>> buildPool(final ParserBuilder builder) {
169 
170         final List<NdmConstituent<?, ?>> pool = new ArrayList<>();
171 
172         // AEM files
173         for (final String name :
174             Arrays.asList("/ccsds/adm/aem/AEMExample01.txt", "/ccsds/adm/aem/AEMExample02.txt", "/ccsds/adm/aem/AEMExample03.txt",
175                           "/ccsds/adm/aem/AEMExample03.xml", "/ccsds/adm/aem/AEMExample04.txt", "/ccsds/adm/aem/AEMExample05.txt",
176                           "/ccsds/adm/aem/AEMExample07.txt", "/ccsds/adm/aem/AEMExample08.txt", "/ccsds/adm/aem/AEMExample09.txt",
177                           "/ccsds/adm/aem/AEMExample10.txt", "/ccsds/adm/aem/AEMExample11.xml", "/ccsds/adm/aem/AEMExample12.txt",
178                           "/ccsds/adm/aem/AEMExample13.xml", "/ccsds/adm/aem/AEMExample14.txt", "/ccsds/adm/aem/AEMExample15.txt",
179                           "/ccsds/adm/aem/AEMExample16.txt", "/ccsds/adm/aem/AEMExample17.txt")) {
180             final DataSource source = new DataSource(name, () -> NdmWriterTest.class.getResourceAsStream(name));
181             pool.add(builder.buildAemParser().parseMessage(source));
182         }
183 
184         // APM files
185         for (final String name :
186             Arrays.asList("/ccsds/adm/apm/APMExample01.txt",  "/ccsds/adm/apm/APMExample02.txt",  "/ccsds/adm/apm/APMExample02.xml",
187                           "/ccsds/adm/apm/APMExample03.txt",  "/ccsds/adm/apm/APMExample04.txt",  "/ccsds/adm/apm/APMExample05.txt",
188                           "/ccsds/adm/apm/APMExample06.txt",  "/ccsds/adm/apm/APMExample07.txt",  "/ccsds/adm/apm/APMExample08.txt",
189                           "/ccsds/adm/apm/APMExample09.txt",  "/ccsds/adm/apm/APMExample10.txt",  "/ccsds/adm/apm/APMExample11.txt",
190                           "/ccsds/adm/apm/APMExample12.txt")) {
191             final DataSource source = new DataSource(name, () -> NdmWriterTest.class.getResourceAsStream(name));
192             pool.add(builder.buildApmParser().parseMessage(source));
193         }
194 
195         // ACM files
196         for (final String name :
197             Arrays.asList("/ccsds/adm/acm/ACMExample01.txt",  "/ccsds/adm/acm/ACMExample02.txt",  "/ccsds/adm/acm/ACMExample03.txt",
198                           "/ccsds/adm/acm/ACMExample04.txt",  "/ccsds/adm/acm/ACMExample05.txt",  "/ccsds/adm/acm/ACMExample06.txt",
199                           "/ccsds/adm/acm/ACMExample07.txt",  "/ccsds/adm/acm/ACMExample08.txt",  "/ccsds/adm/acm/ACMExample09.txt")) {
200             final DataSource source = new DataSource(name, () -> NdmWriterTest.class.getResourceAsStream(name));
201             pool.add(builder.buildAcmParser().parseMessage(source));
202         }
203 
204         // OCM files
205         for (final String name :
206             Arrays.asList("/ccsds/odm/ocm/OCMExample1.txt",  "/ccsds/odm/ocm/OCMExample2.txt",  "/ccsds/odm/ocm/OCMExample2.xml",
207                           "/ccsds/odm/ocm/OCMExample3.txt",  "/ccsds/odm/ocm/OCMExample4.txt")) {
208             final DataSource source = new DataSource(name, () -> NdmWriterTest.class.getResourceAsStream(name));
209             pool.add(builder.buildOcmParser().parseMessage(source));
210         }
211 
212         // OEM files
213         for (final String name :
214             Arrays.asList("/ccsds/odm/oem/OEMExample1.txt",  "/ccsds/odm/oem/OEMExample2.txt",  "/ccsds/odm/oem/OEMExample3.txt",
215                           "/ccsds/odm/oem/OEMExample3.xml",  "/ccsds/odm/oem/OEMExample4.txt",  "/ccsds/odm/oem/OEMExample5.txt",
216                           "/ccsds/odm/oem/OEMExample6.txt",  "/ccsds/odm/oem/OEMExample8.txt")) {
217             final DataSource source = new DataSource(name, () -> NdmWriterTest.class.getResourceAsStream(name));
218             pool.add(builder.buildOemParser().parseMessage(source));
219         }
220 
221         // OMM files
222         for (final String name :
223             Arrays.asList("/ccsds/odm/omm/OMMExample1.txt",  "/ccsds/odm/omm/OMMExample2.txt",  "/ccsds/odm/omm/OMMExample2.xml",
224                           "/ccsds/odm/omm/OMMExample3.txt",  "/ccsds/odm/omm/OMMExample4.txt",  "/ccsds/odm/omm/OMMExample4.xml")) {
225             final DataSource source = new DataSource(name, () -> NdmWriterTest.class.getResourceAsStream(name));
226             pool.add(builder.buildOmmParser().parseMessage(source));
227         }
228 
229         // OPM files
230         for (final String name :
231             Arrays.asList("/ccsds/odm/opm/OPMExample1.txt",  "/ccsds/odm/opm/OPMExample2.txt",  "/ccsds/odm/opm/OPMExample3.txt",
232                           "/ccsds/odm/opm/OPMExample3.xml",  "/ccsds/odm/opm/OPMExample4.txt",  "/ccsds/odm/opm/OPMExample5.txt",
233                           "/ccsds/odm/opm/OPMExample6.txt",  "/ccsds/odm/opm/OPMExample6.txt",  "/ccsds/odm/opm/OPMExample8.txt")) {
234             final DataSource source = new DataSource(name, () -> NdmWriterTest.class.getResourceAsStream(name));
235             pool.add(builder.buildOpmParser().parseMessage(source));
236         }
237 
238         // TDM files
239         for (final String name :
240             Arrays.asList("/ccsds/tdm/kvn/TDMExample15.txt", "/ccsds/tdm/kvn/TDMExample2.txt",  "/ccsds/tdm/kvn/TDMExample4.txt",
241                           "/ccsds/tdm/kvn/TDMExample6.txt",  "/ccsds/tdm/kvn/TDMExample8.txt",
242                           "/ccsds/tdm/xml/TDMExample15.xml", "/ccsds/tdm/xml/TDMExample2.xml",  "/ccsds/tdm/xml/TDMExample4.xml",
243                           "/ccsds/tdm/xml/TDMExample6.xml",  "/ccsds/tdm/xml/TDMExample8.xml")) {
244             final DataSource source = new DataSource(name, () -> NdmWriterTest.class.getResourceAsStream(name));
245             pool.add(builder.buildTdmParser().parseMessage(source));
246         }
247 
248         return pool;
249 
250     }
251 
252 }