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.tdm;
18  
19  import org.hamcrest.CoreMatchers;
20  import org.hamcrest.MatcherAssert;
21  import org.hipparchus.util.FastMath;
22  import org.junit.jupiter.api.Assertions;
23  import org.junit.jupiter.api.BeforeEach;
24  import org.junit.jupiter.api.Test;
25  import org.orekit.Utils;
26  import org.orekit.data.DataSource;
27  import org.orekit.errors.OrekitException;
28  import org.orekit.errors.OrekitMessages;
29  import org.orekit.files.ccsds.definitions.CelestialBodyFrame;
30  import org.orekit.files.ccsds.definitions.TimeSystem;
31  import org.orekit.files.ccsds.ndm.ParserBuilder;
32  import org.orekit.files.ccsds.ndm.WriterBuilder;
33  import org.orekit.files.ccsds.utils.generation.Generator;
34  import org.orekit.files.ccsds.utils.generation.KvnGenerator;
35  import org.orekit.frames.FramesFactory;
36  import org.orekit.time.AbsoluteDate;
37  import org.orekit.time.TimeScale;
38  import org.orekit.time.TimeScalesFactory;
39  import org.orekit.utils.Constants;
40  
41  import java.io.ByteArrayInputStream;
42  import java.io.CharArrayWriter;
43  import java.io.IOException;
44  import java.net.MalformedURLException;
45  import java.net.URISyntaxException;
46  import java.nio.charset.StandardCharsets;
47  import java.util.ArrayList;
48  import java.util.Collections;
49  import java.util.List;
50  
51  /**
52   * Test class for CCSDS Tracking Data Message parsing.<p>
53   * Examples are taken from Annexe D of
54   * <a href="https://public.ccsds.org/Pubs/503x0b1c1.pdf">CCSDS 503.0-B-1 recommended standard [1]</a> ("Tracking Data Message", Blue Book, Version 1.0, November 2007).<p>
55   * Both KeyValue and XML formats are tested here on equivalent files.
56   * @author mjournot
57   *
58   */
59  public class TdmParserTest {
60  
61      @BeforeEach
62      public void setUp() {
63          Utils.setDataRoot("regular-data");
64      }
65  
66      @Test
67      public void testParseTdmExternalResourceIssue368() {
68          // setup
69          final String name = "/ccsds/tdm/xml/TDM-external-doctype.xml";
70          final DataSource source = new DataSource(name, () -> TdmParserTest.class.getResourceAsStream(name));
71  
72          try {
73              // action
74              new ParserBuilder().withRangeUnitsConverter(null).buildTdmParser().parseMessage(source);
75  
76              // verify
77              Assertions.fail("Expected Exception");
78          } catch (OrekitException e) {
79              // Malformed URL exception indicates external resource was disabled
80              // file not found exception indicates parser tried to load the resource
81              MatcherAssert.assertThat(e.getCause(),
82                      CoreMatchers.instanceOf(MalformedURLException.class));
83          }
84      }
85  
86      @Test
87      public void testParseTdmKeyValueExample2() {
88          // Example 2 of [1]
89          // See Figure D-2: TDM Example: One-Way Data w/Frequency Offset
90          // Data lines number was cut down to 7
91          final String name = "/ccsds/tdm/kvn/TDMExample2.txt";
92          final DataSource source = new DataSource(name, () -> TdmParserTest.class.getResourceAsStream(name));
93          final Tdm file = new ParserBuilder().withRangeUnitsConverter(null).buildTdmParser().parseMessage(source);
94          validateTDMExample2(file);
95      }
96  
97      @Test
98      public void testParseTdmKeyValueExample4() {
99  
100         // Example 4 of [1]
101         // See Figure D-4: TDM Example: Two-Way Ranging Data Only
102         // Data lines number was cut down to 20
103         final String name = "/ccsds/tdm/kvn/TDMExample4.txt";
104         final DataSource source = new DataSource(name, () -> TdmParserTest.class.getResourceAsStream(name));
105         final Tdm file = new ParserBuilder().buildTdmParser().parseMessage(source);
106         validateTDMExample4(file);
107     }
108 
109     @Test
110     public void testParseTdmKeyValueExample6() {
111 
112         // Example 6 of [1]
113         // See Figure D-6: TDM Example: Four-Way Data
114         // Data lines number was cut down to 16
115         final String name = "/ccsds/tdm/kvn/TDMExample6.txt";
116         final DataSource source = new DataSource(name, () -> TdmParserTest.class.getResourceAsStream(name));
117         final Tdm file = new ParserBuilder().withRangeUnitsConverter(null).buildTdmParser().parseMessage(source);
118         validateTDMExample6(file);
119     }
120 
121     @Test
122     public void testParseTdmKeyValueExample8() {
123 
124         // Example 8 of [1]
125         // See Figure D-8: TDM Example: Angles, Range, Doppler Combined in Single TDM
126         // Data lines number was cut down to 18
127         final String name = "/ccsds/tdm/kvn/TDMExample8.txt";
128         final DataSource source = new DataSource(name, () -> TdmParserTest.class.getResourceAsStream(name));
129         final Tdm file = new ParserBuilder().buildTdmParser().parseMessage(source);
130         validateTDMExample8(file);
131     }
132 
133     @Test
134     public void testParseTdmKeyValueExample15() {
135 
136         // Example 15 of [1]
137         // See Figure D-15: TDM Example: Clock Bias/Drift Only
138         final String name = "/ccsds/tdm/kvn/TDMExample15.txt";
139         final DataSource source = new DataSource(name, () -> TdmParserTest.class.getResourceAsStream(name));
140         final Tdm file = new ParserBuilder().withRangeUnitsConverter(null).buildTdmParser().parseMessage(source);
141         validateTDMExample15(file);
142     }
143 
144     @Test
145     public void testParseTdmKeyValueExampleAllKeywordsSequential() {
146 
147         // Testing all TDM keywords
148         final String name = "/ccsds/tdm/kvn/TDMExampleAllKeywordsSequential.txt";
149         final DataSource source = new DataSource(name, () -> TdmParserTest.class.getResourceAsStream(name));
150         final Tdm file = new ParserBuilder().buildTdmParser().parseMessage(source);
151         validateTDMExampleAllKeywordsSequential(file);
152     }
153 
154     @Test
155     public void testParseTdmKeyValueExampleAllKeywordsSingleDiff() {
156 
157         // Testing all TDM keywords
158         final String name = "/ccsds/tdm/kvn/TDMExampleAllKeywordsSingleDiff.txt";
159         final DataSource source = new DataSource(name, () -> TdmParserTest.class.getResourceAsStream(name));
160         final Tdm file = new ParserBuilder().buildTdmParser().parseMessage(source);
161         validateTDMExampleAllKeywordsSingleDiff(file);
162     }
163 
164     @Test
165     public void testParseTdmXmlExample2() {
166 
167         // Example 2 of [1]
168         // See Figure D-2: TDM Example: One-Way Data w/Frequency Offset
169         // Data lines number was cut down to 7
170         final String name = "/ccsds/tdm/xml/TDMExample2.xml";
171         final DataSource source = new DataSource(name, () -> TdmParserTest.class.getResourceAsStream(name));
172         final Tdm file = new ParserBuilder().withRangeUnitsConverter(null).buildTdmParser().parseMessage(source);
173         validateTDMExample2(file);
174     }
175 
176     @Test
177     public void testWriteTdmXmlExample2() throws IOException {
178 
179         // Example 2 of [1]
180         // See Figure D-2: TDM Example: One-Way Data w/Frequency Offset
181         // Data lines number was cut down to 7
182         final String name = "/ccsds/tdm/xml/TDMExample2.xml";
183         final DataSource source = new DataSource(name, () -> TdmParserTest.class.getResourceAsStream(name));
184         final Tdm original = new ParserBuilder().withRangeUnitsConverter(null).buildTdmParser().parseMessage(source);
185 
186         // write the parsed file back to a characters array
187         final CharArrayWriter caw = new CharArrayWriter();
188         final Generator generator = new KvnGenerator(caw, TdmWriter.KVN_PADDING_WIDTH, "dummy",
189                                                      Constants.JULIAN_DAY, 60);
190         new WriterBuilder().withRangeUnitsConverter(null).buildTdmWriter().writeMessage(generator, original);
191 
192         // reparse the written file
193         final byte[]     bytes   = caw.toString().getBytes(StandardCharsets.UTF_8);
194         final DataSource source2 = new DataSource(name, () -> new ByteArrayInputStream(bytes));
195         final Tdm    rebuilt = new ParserBuilder().withRangeUnitsConverter(null).buildTdmParser().parseMessage(source2);
196         validateTDMExample2(rebuilt);
197 
198     }
199 
200     @Test
201     public void testParseTdmXmlExample4() {
202 
203         // Example 4 of [1]
204         // See Figure D-4: TDM Example: Two-Way Ranging Data Only
205         // Data lines number was cut down to 20
206         final String name = "/ccsds/tdm/xml/TDMExample4.xml";
207         final DataSource source = new DataSource(name, () -> TdmParserTest.class.getResourceAsStream(name));
208         final Tdm file = new ParserBuilder().buildTdmParser().parseMessage(source);
209         validateTDMExample4(file);
210     }
211 
212     @Test
213     public void testParseTdmXmlExample6() {
214 
215         // Example 6 of [1]
216         // See Figure D-6: TDM Example: Four-Way Data
217         // Data lines number was cut down to 16
218         final String name = "/ccsds/tdm/xml/TDMExample6.xml";
219         final DataSource source = new DataSource(name, () -> TdmParserTest.class.getResourceAsStream(name));
220         final Tdm file = new ParserBuilder().withRangeUnitsConverter(null).buildTdmParser().parseMessage(source);
221         validateTDMExample6(file);
222     }
223 
224     @Test
225     public void testParseTdmXmlExample8() {
226 
227         // Example 8 of [1]
228         // See Figure D-8: TDM Example: Angles, Range, Doppler Combined in Single TDM
229         // Data lines number was cut down to 18
230         final String name = "/ccsds/tdm/xml/TDMExample8.xml";
231         final DataSource source = new DataSource(name, () -> TdmParserTest.class.getResourceAsStream(name));
232         final Tdm file = new ParserBuilder().buildTdmParser().parseMessage(source);
233         validateTDMExample8(file);
234     }
235 
236     @Test
237     public void testParseTdmXmlExample15() {
238 
239         // Example 15 of [1]
240         // See Figure D-15: TDM Example: Clock Bias/Drift Only
241         final String name = "/ccsds/tdm/xml/TDMExample15.xml";
242         final DataSource source = new DataSource(name, () -> TdmParserTest.class.getResourceAsStream(name));
243         final Tdm file = new ParserBuilder().withRangeUnitsConverter(null).buildTdmParser().parseMessage(source);
244         validateTDMExample15(file);
245     }
246 
247     @Test
248     public void testIssue963() {
249 
250         // Check that a TDM with spaces in between participants in PATH is rejected
251         final String name = "/ccsds/tdm/kvn/TDM-issue963.txt";
252         final DataSource source = new DataSource(name, () -> TdmParserTest.class.getResourceAsStream(name));
253         try {
254             // Number format exception in metadata part
255             new ParserBuilder().buildTdmParser().parseMessage(source);
256             Assertions.fail("An exception should have been thrown");
257         } catch (OrekitException oe) {
258             Assertions.assertEquals(OrekitMessages.UNABLE_TO_PARSE_ELEMENT_IN_FILE, oe.getSpecifier());
259         }
260     }
261 
262     @Test
263     public void testParseTdmXmlExampleAllKeywordsSequential() {
264 
265         // Testing all TDM keywords
266         final String name = "/ccsds/tdm/xml/TDMExampleAllKeywordsSequential.xml";
267         final DataSource source = new DataSource(name, () -> TdmParserTest.class.getResourceAsStream(name));
268         final Tdm file = new ParserBuilder().buildTdmParser().parseMessage(source);
269         validateTDMExampleAllKeywordsSequential(file);
270     }
271 
272     @Test
273     public void testParseTdmXmlExampleAllKeywordsSingleDiff() {
274 
275         // Testing all TDM keywords
276         final String name = "/ccsds/tdm/xml/TDMExampleAllKeywordsSingleDiff.xml";
277         final DataSource source = new DataSource(name, () -> TdmParserTest.class.getResourceAsStream(name));
278         final Tdm file = new ParserBuilder().buildTdmParser().parseMessage(source);
279         validateTDMExampleAllKeywordsSingleDiff(file);
280     }
281 
282     @Test
283     public void testDataNumberFormatErrorTypeKeyValue() {
284         final String name = "/ccsds/tdm/kvn/TDM-data-number-format-error.txt";
285         final DataSource source = new DataSource(name, () -> TdmParserTest.class.getResourceAsStream(name));
286         try {
287             // Number format exception in data part
288             new ParserBuilder().buildTdmParser().parseMessage(source);
289             Assertions.fail("An exception should have been thrown");
290         } catch (OrekitException oe) {
291             Assertions.assertEquals(OrekitMessages.UNABLE_TO_PARSE_ELEMENT_IN_FILE, oe.getSpecifier());
292             Assertions.assertEquals("RECEIVE_FREQ_1", oe.getParts()[0]);
293             Assertions.assertEquals(26, oe.getParts()[1]);
294             Assertions.assertEquals(name, oe.getParts()[2]);
295         }
296     }
297 
298     @Test
299     public void testDataNumberFormatErrorTypeXml() {
300         try {
301             // Number format exception in data part
302             final String name = "/ccsds/tdm/xml/TDM-data-number-format-error.xml";
303             final DataSource source = new DataSource(name, () -> TdmParserTest.class.getResourceAsStream(name));
304             new ParserBuilder().buildTdmParser().parseMessage(source);
305             Assertions.fail("An exception should have been thrown");
306         } catch (OrekitException oe) {
307             Assertions.assertEquals(OrekitMessages.UNABLE_TO_PARSE_ELEMENT_IN_FILE, oe.getSpecifier());
308             Assertions.assertEquals("RECEIVE_FREQ_1", oe.getParts()[0]);
309             Assertions.assertEquals(47, oe.getParts()[1]);
310             Assertions.assertEquals("/ccsds/tdm/xml/TDM-data-number-format-error.xml", oe.getParts()[2]);
311         }
312     }
313 
314     @Test
315     public void testMetaDataNumberFormatErrorTypeKeyValue() {
316         try {
317             // Number format exception in metadata part
318             final String name = "/ccsds/tdm/kvn/TDM-metadata-number-format-error.txt";
319             final DataSource source = new DataSource(name, () -> TdmParserTest.class.getResourceAsStream(name));
320             new ParserBuilder().withRangeUnitsConverter(null).buildTdmParser().parseMessage(source);
321             Assertions.fail("An Orekit Exception \"UNABLE_TO_PARSE_LINE_IN_FILE\" should have been thrown");
322         } catch (OrekitException oe) {
323             Assertions.assertEquals(OrekitMessages.UNABLE_TO_PARSE_ELEMENT_IN_FILE, oe.getSpecifier());
324             Assertions.assertEquals("TRANSMIT_DELAY_1", oe.getParts()[0]);
325             Assertions.assertEquals(17, oe.getParts()[1]);
326             Assertions.assertEquals("/ccsds/tdm/kvn/TDM-metadata-number-format-error.txt", oe.getParts()[2]);
327         }
328     }
329 
330     @Test
331     public void testMetaDataNumberFormatErrorTypeXml() {
332         try {
333             // Number format exception in metadata part
334             final String name = "/ccsds/tdm/xml/TDM-metadata-number-format-error.xml";
335             final DataSource source = new DataSource(name, () -> TdmParserTest.class.getResourceAsStream(name));
336             new ParserBuilder().withRangeUnitsConverter(null).buildTdmParser().parseMessage(source);
337             Assertions.fail("An exception should have been thrown");
338         } catch (OrekitException oe) {
339             Assertions.assertEquals(OrekitMessages.UNABLE_TO_PARSE_ELEMENT_IN_FILE, oe.getSpecifier());
340             Assertions.assertEquals("TRANSMIT_DELAY_1", oe.getParts()[0]);
341             Assertions.assertEquals(24, oe.getParts()[1]);
342             Assertions.assertEquals("/ccsds/tdm/xml/TDM-metadata-number-format-error.xml", oe.getParts()[2]);
343         }
344     }
345 
346     @Test
347     public void testNonExistentFile() throws URISyntaxException {
348         // Try parsing a file that does not exist
349         final String realName = "/ccsds/odm/oem/OEMExample2.txt";
350         final String wrongName = realName + "xxxxx";
351         final DataSource source = new DataSource(wrongName, () -> TdmParserTest.class.getResourceAsStream(wrongName));
352         try {
353             new ParserBuilder().withRangeUnitsConverter(null).buildTdmParser().parseMessage(source);
354             Assertions.fail("An exception should have been thrown");
355         } catch (OrekitException oe) {
356             Assertions.assertEquals(OrekitMessages.UNABLE_TO_FIND_FILE, oe.getSpecifier());
357             Assertions.assertEquals(wrongName, oe.getParts()[0]);
358         }
359     }
360 
361     @Test
362     public void testInconsistentTimeSystemsKeyValue() {
363         // Inconsistent time systems between two sets of data
364         final String name = "/ccsds/tdm/kvn/TDM-inconsistent-time-systems.txt";
365         final DataSource source = new DataSource(name, () -> TdmParserTest.class.getResourceAsStream(name));
366         Tdm file = new ParserBuilder().withRangeUnitsConverter(null).buildTdmParser().parseMessage(source);
367         Assertions.assertEquals(3, file.getSegments().size());
368         Assertions.assertEquals(TimeSystem.UTC, file.getSegments().get(0).getMetadata().getTimeSystem());
369         Assertions.assertEquals(TimeSystem.TCG, file.getSegments().get(1).getMetadata().getTimeSystem());
370         Assertions.assertEquals(TimeSystem.UTC, file.getSegments().get(2).getMetadata().getTimeSystem());
371     }
372 
373     @Test
374     public void testInconsistentTimeSystemsXml() {
375         // Inconsistent time systems between two sets of data
376         final String name = "/ccsds/tdm/xml/TDM-inconsistent-time-systems.xml";
377         final DataSource source = new DataSource(name, () -> TdmParserTest.class.getResourceAsStream(name));
378         Tdm file = new ParserBuilder().withRangeUnitsConverter(null).buildTdmParser().parseMessage(source);
379         Assertions.assertEquals(3, file.getSegments().size());
380         Assertions.assertEquals(TimeSystem.UTC, file.getSegments().get(0).getMetadata().getTimeSystem());
381         Assertions.assertEquals(TimeSystem.TCG, file.getSegments().get(1).getMetadata().getTimeSystem());
382         Assertions.assertEquals(TimeSystem.UTC, file.getSegments().get(2).getMetadata().getTimeSystem());
383     }
384 
385     @Test
386     public void testWrongDataKeywordKeyValue() throws URISyntaxException {
387         // Unknown CCSDS keyword was read in data part
388         final String name = "/ccsds/tdm/kvn/TDM-data-wrong-keyword.txt";
389         final DataSource source = new DataSource(name, () -> TdmParserTest.class.getResourceAsStream(name));
390         try {
391             new ParserBuilder().withRangeUnitsConverter(null).buildTdmParser().parseMessage(source);
392             Assertions.fail("An exception should have been thrown");
393         } catch (OrekitException oe) {
394             Assertions.assertEquals(OrekitMessages.CCSDS_UNEXPECTED_KEYWORD, oe.getSpecifier());
395             Assertions.assertEquals(26, oe.getParts()[0]);
396             Assertions.assertEquals("/ccsds/tdm/kvn/TDM-data-wrong-keyword.txt", oe.getParts()[1], "%s");
397             Assertions.assertEquals("WRONG_KEYWORD", oe.getParts()[2]);
398         }
399     }
400 
401     @Test
402     public void testWrongDataKeywordXml() throws URISyntaxException {
403         // Unknown CCSDS keyword was read in data part
404         final String name = "/ccsds/tdm/xml/TDM-data-wrong-keyword.xml";
405         final DataSource source = new DataSource(name, () -> TdmParserTest.class.getResourceAsStream(name));
406         try {
407             new ParserBuilder().withRangeUnitsConverter(null).buildTdmParser().parseMessage(source);
408             Assertions.fail("An exception should have been thrown");
409         } catch (OrekitException oe) {
410             Assertions.assertEquals(OrekitMessages.CCSDS_UNEXPECTED_KEYWORD, oe.getSpecifier());
411             Assertions.assertEquals(47, oe.getParts()[0]);
412             Assertions.assertEquals(name, oe.getParts()[1]);
413             Assertions.assertEquals("WRONG_KEYWORD", oe.getParts()[2]);
414         }
415     }
416 
417     @Test
418     public void testWrongMetaDataKeywordKeyValue() throws URISyntaxException {
419         // Unknown CCSDS keyword was read in data part
420         final String name = "/ccsds/tdm/kvn/TDM-metadata-wrong-keyword.txt";
421         final DataSource source = new DataSource(name, () -> TdmParserTest.class.getResourceAsStream(name));
422         try {
423             new ParserBuilder().withRangeUnitsConverter(null).buildTdmParser().parseMessage(source);
424             Assertions.fail("An exception should have been thrown");
425         } catch (OrekitException oe) {
426             Assertions.assertEquals(OrekitMessages.CCSDS_UNEXPECTED_KEYWORD, oe.getSpecifier());
427             Assertions.assertEquals(16, oe.getParts()[0]);
428             Assertions.assertEquals("/ccsds/tdm/kvn/TDM-metadata-wrong-keyword.txt", oe.getParts()[1]);
429             Assertions.assertEquals("WRONG_KEYWORD", oe.getParts()[2]);
430         }
431     }
432 
433     @Test
434     public void testWrongMetaDataKeywordXml() throws URISyntaxException {
435         // Unknown CCSDS keyword was read in data part
436         final String name = "/ccsds/tdm/xml/TDM-metadata-wrong-keyword.xml";
437         final DataSource source = new DataSource(name, () -> TdmParserTest.class.getResourceAsStream(name));
438         try {
439             new ParserBuilder().withRangeUnitsConverter(null).buildTdmParser().parseMessage(source);
440             Assertions.fail("An exception should have been thrown");
441         } catch (OrekitException oe) {
442             Assertions.assertEquals(OrekitMessages.CCSDS_UNEXPECTED_KEYWORD, oe.getSpecifier());
443             Assertions.assertEquals(23, oe.getParts()[0]);
444             Assertions.assertEquals("/ccsds/tdm/xml/TDM-metadata-wrong-keyword.xml", oe.getParts()[1]);
445             Assertions.assertEquals("WRONG_KEYWORD", oe.getParts()[2]);
446         }
447     }
448 
449     @Test
450     public void testWrongTimeSystemKeyValue() {
451         // Time system not implemented CCSDS keyword was read in data part
452         final String name = "/ccsds/tdm/kvn/TDM-metadata-timesystem-not-implemented.txt";
453         final DataSource source = new DataSource(name, () -> TdmParserTest.class.getResourceAsStream(name));
454         try {
455             new ParserBuilder().withRangeUnitsConverter(null).buildTdmParser().parseMessage(source);
456             Assertions.fail("An exception should have been thrown");
457         } catch (OrekitException oe) {
458             Assertions.assertEquals(OrekitMessages.CCSDS_TIME_SYSTEM_NOT_IMPLEMENTED, oe.getSpecifier());
459             Assertions.assertEquals("WRONG-TIME-SYSTEM", oe.getParts()[0]);
460         }
461     }
462 
463     @Test
464     public void testWrongTimeSystemXml() {
465         // Time system not implemented CCSDS keyword was read in data part
466         final String name = "/ccsds/tdm/xml/TDM-metadata-timesystem-not-implemented.xml";
467         final DataSource source = new DataSource(name, () -> TdmParserTest.class.getResourceAsStream(name));
468         try {
469             new ParserBuilder().withRangeUnitsConverter(null).buildTdmParser().parseMessage(source);
470             Assertions.fail("An exception should have been thrown");
471         } catch (OrekitException oe) {
472             Assertions.assertEquals(OrekitMessages.CCSDS_TIME_SYSTEM_NOT_IMPLEMENTED, oe.getSpecifier());
473             Assertions.assertEquals("WRONG-TIME-SYSTEM", oe.getParts()[0]);
474         }
475     }
476 
477     @Test
478     public void testMissingTimeSystemXml() {
479         // Time system not implemented CCSDS keyword was read in data part
480         final String name = "/ccsds/tdm/xml/TDM-missing-timesystem.xml";
481         final DataSource source = new DataSource(name, () -> TdmParserTest.class.getResourceAsStream(name));
482         try {
483             new ParserBuilder().withRangeUnitsConverter(null).buildTdmParser().parseMessage(source);
484             Assertions.fail("An exception should have been thrown");
485         } catch (OrekitException oe) {
486             Assertions.assertEquals(OrekitMessages.CCSDS_TIME_SYSTEM_NOT_READ_YET, oe.getSpecifier());
487             Assertions.assertEquals(18, oe.getParts()[0]);
488         }
489     }
490 
491     @Test
492     public void testMissingPArticipants() {
493         final String name = "/ccsds/tdm/xml/TDM-missing-participants.xml";
494         final DataSource source = new DataSource(name, () -> TdmParserTest.class.getResourceAsStream(name));
495         try {
496             new ParserBuilder().withRangeUnitsConverter(null).buildTdmParser().parseMessage(source);
497             Assertions.fail("An exception should have been thrown");
498         } catch (OrekitException oe) {
499             Assertions.assertEquals(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, oe.getSpecifier());
500             Assertions.assertEquals(TdmMetadataKey.PARTICIPANT_1, oe.getParts()[0]);
501         }
502     }
503 
504     @Test
505     public void testInconsistentDataLineKeyValue() {
506         // Inconsistent data line in KeyValue file (3 fields after keyword instead of 2)
507         final String name = "/ccsds/tdm/kvn/TDM-data-inconsistent-line.txt";
508         final DataSource source = new DataSource(name, () -> TdmParserTest.class.getResourceAsStream(name));
509         try {
510             new ParserBuilder().withRangeUnitsConverter(null).buildTdmParser().parseMessage(source);
511             Assertions.fail("An exception should have been thrown");
512         } catch (OrekitException oe) {
513             Assertions.assertEquals(OrekitMessages.UNABLE_TO_PARSE_ELEMENT_IN_FILE, oe.getSpecifier());
514             Assertions.assertEquals("RECEIVE_FREQ_1", oe.getParts()[0]);
515             Assertions.assertEquals(25, oe.getParts()[1]);
516             Assertions.assertEquals("/ccsds/tdm/kvn/TDM-data-inconsistent-line.txt", oe.getParts()[2]);
517         }
518     }
519 
520     @Test
521     public void testInconsistentDataBlockXml() {
522         // Inconsistent data block in XML file
523         final String name = "/ccsds/tdm/xml/TDM-data-inconsistent-block.xml";
524         final DataSource source = new DataSource(name, () -> TdmParserTest.class.getResourceAsStream(name));
525         try {
526             new ParserBuilder().withRangeUnitsConverter(null).buildTdmParser().parseMessage(source);
527             Assertions.fail("An exception should have been thrown");
528         } catch (OrekitException oe) {
529             Assertions.assertEquals(OrekitMessages.UNABLE_TO_PARSE_ELEMENT_IN_FILE, oe.getSpecifier());
530             Assertions.assertEquals("TRANSMIT_FREQ_2", oe.getParts()[0]);
531             Assertions.assertEquals(32, oe.getParts()[1]);
532             Assertions.assertEquals("/ccsds/tdm/xml/TDM-data-inconsistent-block.xml", oe.getParts()[2]);
533         }
534     }
535 
536     /**
537      * Validation function for example 2.
538      * @param file Parsed TDM to validate
539      */
540     private void validateTDMExample2(Tdm file) {
541         final TimeScale utc = TimeScalesFactory.getUTC();
542 
543         // Header
544         Assertions.assertEquals(1.0, file.getHeader().getFormatVersion(), 0.0);
545         Assertions.assertEquals(new AbsoluteDate("2005-160T20:15:00", utc).durationFrom(file.getHeader().getCreationDate()), 0.0, 0.0);
546         Assertions.assertEquals("NASA/JPL",file.getHeader().getOriginator());
547         final List<String> headerComment = new ArrayList<String>();
548         headerComment.add("TDM example created by yyyyy-nnnA Nav Team (NASA/JPL)");
549         headerComment.add("StarTrek 1-way data, Ka band down");
550         Assertions.assertEquals(headerComment, file.getHeader().getComments());
551 
552         // Meta-Data
553         final TdmMetadata metadata = file.getSegments().get(0).getMetadata();
554 
555         Assertions.assertEquals("UTC", metadata.getTimeSystem().name());
556         Assertions.assertEquals(0.0, new AbsoluteDate("2005-159T17:41:00", utc).durationFrom(metadata.getStartTime()), 0.0);
557         Assertions.assertEquals(0.0, new AbsoluteDate("2005-159T17:41:40", utc).durationFrom(metadata.getStopTime()), 0.0);
558         Assertions.assertEquals("DSS-25", metadata.getParticipants().get(1));
559         Assertions.assertEquals("yyyy-nnnA", metadata.getParticipants().get(2));
560         Assertions.assertEquals(TrackingMode.SEQUENTIAL, metadata.getMode());
561         Assertions.assertArrayEquals(new int[] { 2, 1 }, metadata.getPath());
562         Assertions.assertEquals(1.0, metadata.getIntegrationInterval(), 0.0);
563         Assertions.assertEquals(IntegrationReference.MIDDLE, metadata.getIntegrationRef());
564         Assertions.assertEquals(32021035200.0, metadata.getFreqOffset(), 0.0);
565         Assertions.assertEquals(0.000077, metadata.getTransmitDelays().get(1), 0.0);
566         Assertions.assertEquals(0.000077, metadata.getReceiveDelays().get(1), 0.0);
567         Assertions.assertEquals(DataQuality.RAW, metadata.getDataQuality());
568         final List<String> metaDataComment = new ArrayList<String>();
569         metaDataComment.add("This is a meta-data comment");
570         Assertions.assertEquals(metaDataComment, metadata.getComments());
571 
572         // Data
573         final List<Observation> observations = file.getSegments().get(0).getData().getObservations();
574 
575         // Reference data
576         final String[] keywords = {"TRANSMIT_FREQ_2", "RECEIVE_FREQ_1", "RECEIVE_FREQ_1", "RECEIVE_FREQ_1",
577             "RECEIVE_FREQ_1", "RECEIVE_FREQ_1", "RECEIVE_FREQ_1"};
578 
579         final String[] epochs   = {"2005-159T17:41:00", "2005-159T17:41:00", "2005-159T17:41:01", "2005-159T17:41:02",
580             "2005-159T17:41:03", "2005-159T17:41:04", "2005-159T17:41:05"};
581 
582         final double[] values   = {32023442781.733, -409.2735, -371.1568, -333.0551,
583             -294.9673, -256.9054, -218.7951};
584         // Check consistency
585         for (int i = 0; i < keywords.length; i++) {
586             Assertions.assertEquals(keywords[i], observations.get(i).getType().name());
587             Assertions.assertEquals(new AbsoluteDate(epochs[i], utc).durationFrom(observations.get(i).getEpoch()), 0.0, 0.0);
588             Assertions.assertEquals(values[i], observations.get(i).getMeasurement(), 0.0);
589         }
590 
591         // Comment
592         final List<String> dataComment = new ArrayList<String>();
593         dataComment.add("This is a data comment");
594         Assertions.assertEquals(dataComment, file.getSegments().get(0).getData().getComments());
595 
596         // check so global setters that are not used by parser (it uses successive add instead)
597 
598         metadata.setParticipants(Collections.singletonMap(12, "p12"));
599         Assertions.assertNull(metadata.getParticipants().get(1));
600         Assertions.assertEquals("p12", metadata.getParticipants().get(12));
601         metadata.setTransmitDelays(Collections.singletonMap(12, 1.25));
602         Assertions.assertNull(metadata.getTransmitDelays().get(1));
603         Assertions.assertEquals(1.25, metadata.getTransmitDelays().get(12).doubleValue(), 1.0e-15);
604         metadata.setReceiveDelays(Collections.singletonMap(12, 2.5));
605         Assertions.assertNull(metadata.getReceiveDelays().get(1));
606         Assertions.assertEquals(2.5, metadata.getReceiveDelays().get(12).doubleValue(), 1.0e-15);
607 
608     }
609 
610     /**
611      * Validation function for example 4.
612      * @param file Parsed TDM to validate
613      */
614     private void validateTDMExample4(Tdm file) {
615 
616         final TimeScale utc = TimeScalesFactory.getUTC();
617 
618         // Header
619         Assertions.assertEquals(1.0, file.getHeader().getFormatVersion(), 0.0);
620         Assertions.assertEquals(new AbsoluteDate("2005-191T23:00:00", utc).durationFrom(file.getHeader().getCreationDate()), 0.0, 0.0);
621         Assertions.assertEquals("NASA/JPL",file.getHeader().getOriginator());
622         final List<String> headerComment = new ArrayList<String>();
623         headerComment.add("TDM example created by yyyyy-nnnA Nav Team (NASA/JPL)");
624         Assertions.assertEquals(headerComment, file.getHeader().getComments());
625 
626         // Meta-Data
627         final TdmMetadata metadata = file.getSegments().get(0).getMetadata();
628 
629         Assertions.assertEquals("UTC", metadata.getTimeSystem().name());
630         Assertions.assertEquals("DSS-24", metadata.getParticipants().get(1));
631         Assertions.assertEquals("yyyy-nnnA", metadata.getParticipants().get(2));
632         Assertions.assertEquals(TrackingMode.SEQUENTIAL, metadata.getMode());
633         Assertions.assertArrayEquals(new int[] { 1, 2, 1 }, metadata.getPath());
634         Assertions.assertEquals(IntegrationReference.START, metadata.getIntegrationRef());
635         Assertions.assertEquals(RangeMode.COHERENT, metadata.getRangeMode());
636         Assertions.assertEquals(2.0e+26, metadata.getRawRangeModulus(), 0.0);
637         Assertions.assertEquals(2.0e+26, metadata.getRangeModulus(new IdentityConverter()), 0.0);
638         Assertions.assertEquals(RangeUnits.RU, metadata.getRangeUnits());
639         Assertions.assertEquals(7.7e-5, metadata.getTransmitDelays().get(1), 0.0);
640         Assertions.assertEquals(0.0, metadata.getTransmitDelays().get(2), 0.0);
641         Assertions.assertEquals(7.7e-5, metadata.getReceiveDelays().get(1), 0.0);
642         Assertions.assertEquals(0.0, metadata.getReceiveDelays().get(2), 0.0);
643         Assertions.assertEquals(46.7741, metadata.getCorrectionRange(new IdentityConverter()), 0.0);
644         Assertions.assertEquals(46.7741, metadata.getRawCorrectionRange(), 0.0);
645         Assertions.assertEquals(CorrectionApplied.YES, metadata.getCorrectionsApplied());
646         final List<String> metaDataComment = new ArrayList<String>();
647         metaDataComment.add("Range correction applied is range calibration to DSS-24.");
648         metaDataComment.add("Estimated RTLT at begin of pass = 950 seconds");
649         metaDataComment.add("Antenna Z-height correction 0.0545 km applied to uplink signal");
650         metaDataComment.add("Antenna Z-height correction 0.0189 km applied to downlink signal");
651         Assertions.assertEquals(metaDataComment, metadata.getComments());
652 
653         // Data
654         final List<Observation> observations = file.getSegments().get(0).getData().getObservations();
655 
656         // Reference data
657         final String[] keywords = {"TRANSMIT_FREQ_1", "TRANSMIT_FREQ_RATE_1", "RANGE", "PR_N0",
658             "TRANSMIT_FREQ_1", "TRANSMIT_FREQ_RATE_1", "RANGE", "PR_N0",
659             "TRANSMIT_FREQ_1", "TRANSMIT_FREQ_RATE_1", "RANGE", "PR_N0",
660             "TRANSMIT_FREQ_1", "TRANSMIT_FREQ_RATE_1", "RANGE", "PR_N0"};
661 
662         final String[] epochs   = {"2005-191T00:31:51", "2005-191T00:31:51", "2005-191T00:31:51", "2005-191T00:31:51",
663             "2005-191T00:34:48", "2005-191T00:34:48", "2005-191T00:34:48", "2005-191T00:34:48",
664             "2005-191T00:37:45", "2005-191T00:37:45", "2005-191T00:37:45", "2005-191T00:37:45",
665             "2005-191T00:40:42", "2005-191T00:40:42", "2005-191T00:40:42", "2005-191T00:40:42",
666             "2005-191T00:58:24", "2005-191T00:58:24", "2005-191T00:58:24", "2005-191T00:58:24"};
667 
668         final double[] values   = {7180064367.3536 , 0.59299, 39242998.5151986, 28.52538,
669             7180064472.3146 , 0.59305, 61172265.3115234, 28.39347,
670             7180064577.2756 , 0.59299, 15998108.8168328, 28.16193,
671             7180064682.2366 , 0.59299, 37938284.4138008, 29.44597,
672             7180065327.56141, 0.62085, 35478729.4012973, 30.48199};
673         // Check consistency
674         for (int i = 0; i < keywords.length; i++) {
675             Assertions.assertEquals(keywords[i], observations.get(i).getType().name());
676             Assertions.assertEquals(new AbsoluteDate(epochs[i], utc).durationFrom(observations.get(i).getEpoch()), 0.0, 0.0);
677             Assertions.assertEquals(values[i], observations.get(i).getMeasurement(), 0.0);
678         }
679         // Comment
680         final List<String> dataComment = new ArrayList<String>();
681         dataComment.add("This is a data comment");
682         Assertions.assertEquals(dataComment, file.getSegments().get(0).getData().getComments());
683     }
684 
685     /**
686      * Validation function for example 6.
687      * @param file Parsed TDM to validate
688      */
689     private void validateTDMExample6(Tdm file) {
690 
691         final TimeScale utc = TimeScalesFactory.getUTC();
692 
693         // Header
694         Assertions.assertEquals(1.0, file.getHeader().getFormatVersion(), 0.0);
695         Assertions.assertEquals(new AbsoluteDate("1998-06-10T01:00:00", utc).durationFrom(file.getHeader().getCreationDate()), 0.0, 0.0);
696         Assertions.assertEquals("JAXA",file.getHeader().getOriginator());
697         final List<String> headerComment = new ArrayList<String>();
698         headerComment.add("TDM example created by yyyyy-nnnA Nav Team (JAXA)");
699         Assertions.assertEquals(headerComment, file.getHeader().getComments());
700 
701         // Meta-Data
702         final TdmMetadata metadata = file.getSegments().get(0).getMetadata();
703 
704         Assertions.assertEquals("UTC", metadata.getTimeSystem().name());
705         Assertions.assertEquals(new AbsoluteDate("1998-06-10T00:57:37", utc).durationFrom(metadata.getStartTime()), 0.0, 0.0);
706         Assertions.assertEquals(new AbsoluteDate("1998-06-10T00:57:44", utc).durationFrom(metadata.getStopTime()), 0.0, 0.0);
707         Assertions.assertEquals("NORTH", metadata.getParticipants().get(1));
708         Assertions.assertEquals("F07R07", metadata.getParticipants().get(2));
709         Assertions.assertEquals("E7", metadata.getParticipants().get(3));
710         Assertions.assertEquals(TrackingMode.SEQUENTIAL, metadata.getMode());
711         Assertions.assertArrayEquals(new int[] { 1, 2, 3, 2, 1 }, metadata.getPath());
712         Assertions.assertEquals(1.0, metadata.getIntegrationInterval(), 0.0);
713         Assertions.assertEquals(IntegrationReference.MIDDLE, metadata.getIntegrationRef());
714         Assertions.assertEquals(RangeMode.CONSTANT, metadata.getRangeMode());
715         Assertions.assertEquals(1.0, metadata.getRawRangeModulus(), 0.0);
716         Assertions.assertEquals(1000.0, metadata.getRangeModulus(new IdentityConverter()), 0.0);
717         Assertions.assertEquals(RangeUnits.km, metadata.getRangeUnits());
718         Assertions.assertEquals(AngleType.AZEL, metadata.getAngleType());
719         Assertions.assertEquals(2.0, metadata.getRawCorrectionRange(), 0.0);
720         Assertions.assertEquals(2000.0, metadata.getCorrectionRange(new IdentityConverter()), 0.0);
721         Assertions.assertEquals(CorrectionApplied.YES, metadata.getCorrectionsApplied());
722 
723         // Data
724         final List<Observation> observations = file.getSegments().get(0).getData().getObservations();
725 
726         // Reference data
727         final String[] keywords = {"RANGE", "ANGLE_1", "ANGLE_2", "TRANSMIT_FREQ_1", "RECEIVE_FREQ",
728             "RANGE", "ANGLE_1", "ANGLE_2", "TRANSMIT_FREQ_1", "RECEIVE_FREQ",
729             "RANGE", "ANGLE_1", "ANGLE_2", "TRANSMIT_FREQ_1", "RECEIVE_FREQ",
730             "RANGE", "ANGLE_1", "ANGLE_2", "TRANSMIT_FREQ_1", "RECEIVE_FREQ",};
731 
732         final String[] epochs   = {"1998-06-10T00:57:37", "1998-06-10T00:57:37", "1998-06-10T00:57:37", "1998-06-10T00:57:37", "1998-06-10T00:57:37",
733             "1998-06-10T00:57:38", "1998-06-10T00:57:38", "1998-06-10T00:57:38", "1998-06-10T00:57:38", "1998-06-10T00:57:38",
734             "1998-06-10T00:57:39", "1998-06-10T00:57:39", "1998-06-10T00:57:39", "1998-06-10T00:57:39", "1998-06-10T00:57:39",
735             "1998-06-10T00:57:44", "1998-06-10T00:57:44", "1998-06-10T00:57:44", "1998-06-10T00:57:44", "1998-06-10T00:57:44",};
736 
737         final double[] values   = { 80452754.2, FastMath.toRadians(256.64002393), FastMath.toRadians(13.38100016), 2106395199.07917, 2287487999.0,
738             80452736.8, FastMath.toRadians(256.64002393), FastMath.toRadians(13.38100016), 2106395199.07917, 2287487999.0,
739             80452719.7, FastMath.toRadians(256.64002393), FastMath.toRadians(13.38100016), 2106395199.07917, 2287487999.0,
740             80452633.1, FastMath.toRadians(256.64002393), FastMath.toRadians(13.38100016), 2106395199.07917, 2287487999.0};
741         // Check consistency
742         for (int i = 0; i < keywords.length; i++) {
743             Assertions.assertEquals(keywords[i], observations.get(i).getType().name());
744             Assertions.assertEquals(new AbsoluteDate(epochs[i], utc).durationFrom(observations.get(i).getEpoch()), 0.0, 0.0);
745             Assertions.assertEquals(values[i], observations.get(i).getMeasurement(), 1.0e-12 * FastMath.abs(values[i]));
746         }
747         // Comment
748         final List<String> dataComment = new ArrayList<String>();
749         dataComment.add("This is a data comment");
750         Assertions.assertEquals(dataComment, file.getSegments().get(0).getData().getComments());
751     }
752 
753     /**
754      * Validation function for example 8.
755      * @param file Parsed TDM to validate
756      */
757     private void validateTDMExample8(Tdm file) {
758 
759         final TimeScale utc = TimeScalesFactory.getUTC();
760 
761         // Header
762         Assertions.assertEquals(1.0, file.getHeader().getFormatVersion(), 0.0);
763         Assertions.assertEquals(new AbsoluteDate("2007-08-30T12:01:44.749", utc).durationFrom(file.getHeader().getCreationDate()), 0.0, 0.0);
764         Assertions.assertEquals("GSOC",file.getHeader().getOriginator());
765         final List<String> headerComment = new ArrayList<String>();
766         headerComment.add("GEOSCX INP");
767         Assertions.assertEquals(headerComment, file.getHeader().getComments());
768 
769         // Meta-Data 1
770         final TdmMetadata metadata = file.getSegments().get(0).getMetadata();
771 
772         Assertions.assertEquals("UTC", metadata.getTimeSystem().name());
773         Assertions.assertEquals(new AbsoluteDate("2007-08-29T07:00:02.000", utc).durationFrom(metadata.getStartTime()), 0.0, 0.0);
774         Assertions.assertEquals(new AbsoluteDate("2007-08-29T14:00:02.000", utc).durationFrom(metadata.getStopTime()), 0.0, 0.0);
775         Assertions.assertEquals("HBSTK", metadata.getParticipants().get(1));
776         Assertions.assertEquals("SAT", metadata.getParticipants().get(2));
777         Assertions.assertEquals(TrackingMode.SEQUENTIAL, metadata.getMode());
778         Assertions.assertArrayEquals(new int[] { 1, 2, 1 }, metadata.getPath());
779         Assertions.assertEquals(1.0, metadata.getIntegrationInterval(), 0.0);
780         Assertions.assertEquals(IntegrationReference.END, metadata.getIntegrationRef());
781         Assertions.assertEquals(AngleType.XSYE, metadata.getAngleType());
782         Assertions.assertEquals(DataQuality.RAW, metadata.getDataQuality());
783         final List<String> metaDataComment = new ArrayList<String>();
784         metaDataComment.add("This is a meta-data comment");
785         Assertions.assertEquals(metaDataComment, metadata.getComments());
786 
787         // Data 1
788         final List<Observation> observations = file.getSegments().get(0).getData().getObservations();
789 
790         // Reference data 1
791         final String[] keywords = {"DOPPLER_INTEGRATED", "ANGLE_1", "ANGLE_2",
792             "DOPPLER_INTEGRATED", "ANGLE_1", "ANGLE_2",
793             "DOPPLER_INTEGRATED", "ANGLE_1", "ANGLE_2"};
794 
795         final String[] epochs   = {"2007-08-29T07:00:02.000", "2007-08-29T07:00:02.000", "2007-08-29T07:00:02.000",
796             "2007-08-29T08:00:02.000", "2007-08-29T08:00:02.000", "2007-08-29T08:00:02.000",
797             "2007-08-29T14:00:02.000", "2007-08-29T14:00:02.000", "2007-08-29T14:00:02.000"};
798 
799         final double[] values   = {-1498.776048, FastMath.toRadians(67.01312389), FastMath.toRadians(18.28395556),
800             -2201.305217, FastMath.toRadians(67.01982278), FastMath.toRadians(21.19609167),
801             929.545817, FastMath.toRadians(-89.35626083), FastMath.toRadians(2.78791667)};
802         // Check consistency
803         for (int i = 0; i < keywords.length; i++) {
804             Assertions.assertEquals(keywords[i], observations.get(i).getType().name());
805             Assertions.assertEquals(new AbsoluteDate(epochs[i], utc).durationFrom(observations.get(i).getEpoch()), 0.0, 0.0);
806             Assertions.assertEquals(values[i], observations.get(i).getMeasurement(), 1.0e-12 * FastMath.abs(values[i]));
807         }
808         // Comment
809         final List<String> dataComment = new ArrayList<String>();
810         dataComment.add("This is a data comment");
811         Assertions.assertEquals(dataComment, file.getSegments().get(0).getData().getComments());
812 
813         // Meta-Data 2
814         final TdmMetadata metadata2 = file.getSegments().get(1).getMetadata();
815 
816         Assertions.assertEquals("UTC", metadata.getTimeSystem().name());
817         Assertions.assertEquals(new AbsoluteDate("2007-08-29T06:00:02.000", utc).durationFrom(metadata2.getStartTime()), 0.0, 0.0);
818         Assertions.assertEquals(new AbsoluteDate("2007-08-29T13:00:02.000", utc).durationFrom(metadata2.getStopTime()), 0.0, 0.0);
819         Assertions.assertEquals("WHM1", metadata2.getParticipants().get(1));
820         Assertions.assertEquals("SAT", metadata2.getParticipants().get(2));
821         Assertions.assertEquals(TrackingMode.SEQUENTIAL, metadata2.getMode());
822         Assertions.assertArrayEquals(new int[] { 1, 2, 1 }, metadata2.getPath());
823         Assertions.assertEquals(1.0, metadata2.getIntegrationInterval(), 0.0);
824         Assertions.assertEquals(IntegrationReference.END, metadata2.getIntegrationRef());
825         Assertions.assertEquals(1.0e7, metadata2.getRawRangeModulus(), 0.0);
826         Assertions.assertEquals(1.0e7 * Constants.SPEED_OF_LIGHT, metadata2.getRangeModulus(new IdentityConverter()), 0.0);
827         Assertions.assertEquals(RangeUnits.s, metadata2.getRangeUnits());
828         Assertions.assertEquals(AngleType.AZEL, metadata2.getAngleType());
829         Assertions.assertEquals(DataQuality.RAW, metadata2.getDataQuality());
830         Assertions.assertEquals(2.0, metadata2.getRawCorrectionRange(), 0.0);
831         Assertions.assertEquals(2.0 * Constants.SPEED_OF_LIGHT, metadata2.getCorrectionRange(new IdentityConverter()), 0.0);
832         Assertions.assertEquals(CorrectionApplied.YES, metadata2.getCorrectionsApplied());
833         final List<String> metaDataComment2 = new ArrayList<String>();
834         metaDataComment2.add("This is a meta-data comment");
835         Assertions.assertEquals(metaDataComment2, metadata2.getComments());
836 
837         // Data 2
838         final List<Observation> observations2 = file.getSegments().get(1).getData().getObservations();
839 
840         // Reference data 2
841         final String[] keywords2 = {"RANGE", "DOPPLER_INTEGRATED", "ANGLE_1", "ANGLE_2",
842             "RANGE", "DOPPLER_INTEGRATED", "ANGLE_1", "ANGLE_2",
843             "RANGE", "DOPPLER_INTEGRATED", "ANGLE_1", "ANGLE_2"};
844 
845         final String[] epochs2   = {"2007-08-29T06:00:02.000", "2007-08-29T06:00:02.000", "2007-08-29T06:00:02.000", "2007-08-29T06:00:02.000",
846             "2007-08-29T07:00:02.000", "2007-08-29T07:00:02.000", "2007-08-29T07:00:02.000", "2007-08-29T07:00:02.000",
847             "2007-08-29T13:00:02.000", "2007-08-29T13:00:02.000", "2007-08-29T13:00:02.000", "2007-08-29T13:00:02.000"};
848 
849         final double[] values2   = {4.00165248953670E+04 * Constants.SPEED_OF_LIGHT, -885.640091,  FastMath.toRadians(99.53204250), FastMath.toRadians(1.26724167),
850             3.57238793591890E+04 * Constants.SPEED_OF_LIGHT, -1510.223139, FastMath.toRadians(103.33061750), FastMath.toRadians(4.77875278),
851             3.48156855860090E+04 * Constants.SPEED_OF_LIGHT,  1504.082291, FastMath.toRadians(243.73365222), FastMath.toRadians(8.78254167)};
852         // Check consistency
853         for (int i = 0; i < keywords2.length; i++) {
854             Assertions.assertEquals(keywords2[i], observations2.get(i).getType().name());
855             Assertions.assertEquals(new AbsoluteDate(epochs2[i], utc).durationFrom(observations2.get(i).getEpoch()), 0.0, 0.0);
856             Assertions.assertEquals(values2[i], observations2.get(i).getMeasurement(), 1.0e-12 * FastMath.abs(values2[i]));
857         }
858         // Comment
859         final List<String> dataComment2 = new ArrayList<String>();
860         dataComment2.add("This is a data comment");
861         Assertions.assertEquals(dataComment2, file.getSegments().get(1).getData().getComments());
862     }
863 
864     /**
865      * Validation function for example 15.
866      * @param file Parsed TDM to validate
867      */
868     private void validateTDMExample15(Tdm file) {
869 
870         final TimeScale utc = TimeScalesFactory.getUTC();
871 
872         // Header
873         Assertions.assertEquals(1.0, file.getHeader().getFormatVersion(), 0.0);
874         Assertions.assertEquals(new AbsoluteDate("2005-161T15:45:00", utc).durationFrom(file.getHeader().getCreationDate()), 0.0, 0.0);
875         Assertions.assertEquals("NASA/JPL",file.getHeader().getOriginator());
876         final List<String> headerComment = new ArrayList<String>();
877         headerComment.add("TDM example created by yyyyy-nnnA Nav Team (NASA/JPL)");
878         headerComment.add("The following are clock offsets, in seconds between the");
879         headerComment.add("clocks at each DSN complex relative to UTC(NIST). The offset");
880         headerComment.add("is a mean of readings using several GPS space vehicles in");
881         headerComment.add("common view. Value is \"station clock minus UTC”.");
882         Assertions.assertEquals(headerComment, file.getHeader().getComments());
883 
884         // Meta-Data 1
885         final TdmMetadata metadata = file.getSegments().get(0).getMetadata();
886 
887         Assertions.assertEquals("UTC", metadata.getTimeSystem().name());
888         Assertions.assertEquals(new AbsoluteDate("2005-142T12:00:00", utc).durationFrom(metadata.getStartTime()), 0.0, 0.0);
889         Assertions.assertEquals(new AbsoluteDate("2005-145T12:00:00", utc).durationFrom(metadata.getStopTime()), 0.0, 0.0);
890         Assertions.assertEquals("DSS-10", metadata.getParticipants().get(1));
891         Assertions.assertEquals("UTC-NIST", metadata.getParticipants().get(2));
892         final List<String> metaDataComment = new ArrayList<String>();
893         metaDataComment.add("Note: SPC10 switched back to Maser1 from Maser2 on 2005-142");
894         Assertions.assertEquals(metaDataComment, metadata.getComments());
895 
896         // Data 1
897         final List<Observation> observations = file.getSegments().get(0).getData().getObservations();
898 
899         // Reference data 1
900         final String[] keywords = {"CLOCK_BIAS", "CLOCK_DRIFT",
901             "CLOCK_BIAS", "CLOCK_DRIFT",
902             "CLOCK_BIAS", "CLOCK_DRIFT",
903         "CLOCK_BIAS"};
904 
905         final String[] epochs   = {"2005-142T12:00:00", "2005-142T12:00:00",
906             "2005-143T12:00:00", "2005-143T12:00:00",
907             "2005-144T12:00:00", "2005-144T12:00:00",
908         "2005-145T12:00:00"};
909 
910         final double[] values   = {9.56e-7,  6.944e-14,
911             9.62e-7, -2.083e-13,
912             9.44e-7, -2.778e-13,
913             9.20e-7};
914         // Check consistency
915         for (int i = 0; i < keywords.length; i++) {
916             Assertions.assertEquals(keywords[i], observations.get(i).getType().name());
917             Assertions.assertEquals(new AbsoluteDate(epochs[i], utc).durationFrom(observations.get(i).getEpoch()), 0.0, 0.0);
918             Assertions.assertEquals(values[i], observations.get(i).getMeasurement(), 0.0);
919         }
920         // Comment
921         final List<String> dataComment = new ArrayList<String>();
922         dataComment.add("This is a data comment");
923         Assertions.assertEquals(dataComment, file.getSegments().get(0).getData().getComments());
924 
925 
926         // Meta-Data 2
927         final TdmMetadata metadata2 = file.getSegments().get(1).getMetadata();
928 
929         Assertions.assertEquals("UTC", metadata2.getTimeSystem().name());
930         Assertions.assertEquals(new AbsoluteDate("2005-142T12:00:00", utc).durationFrom(metadata2.getStartTime()), 0.0, 0.0);
931         Assertions.assertEquals(new AbsoluteDate("2005-145T12:00:00", utc).durationFrom(metadata2.getStopTime()), 0.0, 0.0);
932         Assertions.assertEquals("DSS-40", metadata2.getParticipants().get(1));
933         Assertions.assertEquals("UTC-NIST", metadata2.getParticipants().get(2));
934         final List<String> metaDataComment2 = new ArrayList<String>();
935         metaDataComment2.add("This is a meta-data comment");
936         Assertions.assertEquals(metaDataComment2, metadata2.getComments());
937 
938         // Data 2
939         final List<Observation> observations2 = file.getSegments().get(1).getData().getObservations();
940 
941         // Reference data 2
942         // Same keywords and dates than 1
943         final double[] values2   = {-7.40e-7, -3.125e-13,
944             -7.67e-7, -1.620e-13,
945             -7.81e-7, -4.745e-13,
946             -8.22e-7};
947         // Check consistency
948         for (int i = 0; i < keywords.length; i++) {
949             Assertions.assertEquals(keywords[i], observations2.get(i).getType().name());
950             Assertions.assertEquals(new AbsoluteDate(epochs[i], utc).durationFrom(observations2.get(i).getEpoch()), 0.0, 0.0);
951             Assertions.assertEquals(values2[i], observations2.get(i).getMeasurement(), 0.0);
952         }
953         // Comment
954         final List<String> dataComment2 = new ArrayList<String>();
955         dataComment2.add("This is a data comment");
956         Assertions.assertEquals(dataComment2, file.getSegments().get(1).getData().getComments());
957 
958 
959         // Meta-Data 3
960         final TdmMetadata metadata3 = file.getSegments().get(2).getMetadata();
961 
962         Assertions.assertEquals("UTC", metadata3.getTimeSystem().name());
963         Assertions.assertEquals(new AbsoluteDate("2005-142T12:00:00", utc).durationFrom(metadata3.getStartTime()), 0.0, 0.0);
964         Assertions.assertEquals(new AbsoluteDate("2005-145T12:00:00", utc).durationFrom(metadata3.getStopTime()), 0.0, 0.0);
965         Assertions.assertEquals("DSS-60", metadata3.getParticipants().get(1));
966         Assertions.assertEquals("UTC-NIST", metadata3.getParticipants().get(2));
967         final List<String> metaDataComment3 = new ArrayList<String>();
968         metaDataComment3.add("This is a meta-data comment");
969         Assertions.assertEquals(metaDataComment3, metadata3.getComments());
970 
971         // Data 3
972         final List<Observation> observations3 = file.getSegments().get(2).getData().getObservations();
973 
974         // Reference data 2
975         // Same keywords and dates than 1
976         final double[] values3   = {-1.782e-6, 1.736e-13,
977             -1.767e-6, 1.157e-14,
978             -1.766e-6, 8.102e-14,
979             -1.759e-6};
980         // Check consistency
981         for (int i = 0; i < keywords.length; i++) {
982             Assertions.assertEquals(keywords[i], observations3.get(i).getType().name());
983             Assertions.assertEquals(new AbsoluteDate(epochs[i], utc).durationFrom(observations3.get(i).getEpoch()), 0.0, 0.0);
984             Assertions.assertEquals(values3[i], observations3.get(i).getMeasurement(), 0.0);
985         }
986         // Comment
987         final List<String> dataComment3 = new ArrayList<String>();
988         dataComment3.add("This is a data comment");
989         Assertions.assertEquals(dataComment3, file.getSegments().get(2).getData().getComments());
990     }
991 
992     /**
993      * Validation function for example displaying all keywords.
994      * @param file Parsed TDM to validate
995      */
996     private void validateTDMExampleAllKeywordsSequential(Tdm file) {
997         validateTDMExampleAllKeywordsCommon(file);
998         final TdmMetadata metadata = file.getSegments().get(0).getMetadata();
999         Assertions.assertEquals(TrackingMode.SEQUENTIAL, metadata.getMode());
1000         Assertions.assertArrayEquals(new int[] { 2, 1 }, metadata.getPath());
1001     }
1002 
1003     /**
1004      * Validation function for example displaying all keywords.
1005      * @param file Parsed TDM to validate
1006      */
1007     private void validateTDMExampleAllKeywordsSingleDiff(Tdm file) {
1008         validateTDMExampleAllKeywordsCommon(file);
1009         final TdmMetadata metadata = file.getSegments().get(0).getMetadata();
1010         Assertions.assertEquals(TrackingMode.SINGLE_DIFF, metadata.getMode());
1011         Assertions.assertArrayEquals(new int[] { 4, 5 }, metadata.getPath1());
1012         Assertions.assertArrayEquals(new int[] { 3, 2 }, metadata.getPath2());
1013     }
1014 
1015     /**
1016      * Validation function for example displaying all keywords.
1017      * @param file Parsed TDM to validate
1018      */
1019     private void validateTDMExampleAllKeywordsCommon(Tdm file) {
1020 
1021         final TimeScale utc = TimeScalesFactory.getUTC();
1022 
1023         // Header
1024         Assertions.assertEquals(2.0, file.getHeader().getFormatVersion(), 0.0);
1025         Assertions.assertEquals(new AbsoluteDate("2017-06-14T10:53:00.000", utc).durationFrom(file.getHeader().getCreationDate()), 0.0, 0.0);
1026         Assertions.assertEquals("CS GROUP",file.getHeader().getOriginator());
1027         Assertions.assertEquals("04655f62-1ba0-4ca6-92e9-eb3411db3d44", file.getHeader().getMessageId().toLowerCase());
1028         final List<String> headerComment = new ArrayList<String>();
1029         headerComment.add("TDM example created by CS GROUP");
1030         headerComment.add("Testing all TDM known meta-data and data keywords");
1031         Assertions.assertEquals(headerComment, file.getHeader().getComments());
1032 
1033         // Meta-Data
1034         final TdmMetadata metadata = file.getSegments().get(0).getMetadata();
1035         Assertions.assertEquals(1, metadata.getComments().size());
1036         Assertions.assertEquals("All known meta-data keywords displayed", metadata.getComments().get(0));
1037         Assertions.assertEquals(47, metadata.getDataTypes().size());
1038         Assertions.assertEquals(ObservationType.CARRIER_POWER        , metadata.getDataTypes().get( 0));
1039         Assertions.assertEquals(ObservationType.DOPPLER_COUNT        , metadata.getDataTypes().get( 1));
1040         Assertions.assertEquals(ObservationType.DOPPLER_INSTANTANEOUS, metadata.getDataTypes().get( 2));
1041         Assertions.assertEquals(ObservationType.DOPPLER_INTEGRATED   , metadata.getDataTypes().get( 3));
1042         Assertions.assertEquals(ObservationType.PC_N0                , metadata.getDataTypes().get( 4));
1043         Assertions.assertEquals(ObservationType.RECEIVE_PHASE_CT_1   , metadata.getDataTypes().get( 5));
1044         Assertions.assertEquals(ObservationType.RECEIVE_PHASE_CT_2   , metadata.getDataTypes().get( 6));
1045         Assertions.assertEquals(ObservationType.RECEIVE_PHASE_CT_3   , metadata.getDataTypes().get( 7));
1046         Assertions.assertEquals(ObservationType.RECEIVE_PHASE_CT_4   , metadata.getDataTypes().get( 8));
1047         Assertions.assertEquals(ObservationType.RECEIVE_PHASE_CT_5   , metadata.getDataTypes().get( 9));
1048         Assertions.assertEquals(ObservationType.TRANSMIT_PHASE_CT_1  , metadata.getDataTypes().get(10));
1049         Assertions.assertEquals(ObservationType.TRANSMIT_PHASE_CT_2  , metadata.getDataTypes().get(11));
1050         Assertions.assertEquals(ObservationType.TRANSMIT_PHASE_CT_3  , metadata.getDataTypes().get(12));
1051         Assertions.assertEquals(ObservationType.TRANSMIT_PHASE_CT_4  , metadata.getDataTypes().get(13));
1052         Assertions.assertEquals(ObservationType.TRANSMIT_PHASE_CT_5  , metadata.getDataTypes().get(14));
1053         Assertions.assertEquals(ObservationType.PR_N0                , metadata.getDataTypes().get(15));
1054         Assertions.assertEquals(ObservationType.RANGE                , metadata.getDataTypes().get(16));
1055         Assertions.assertEquals(ObservationType.RECEIVE_FREQ_1       , metadata.getDataTypes().get(17));
1056         Assertions.assertEquals(ObservationType.RECEIVE_FREQ_2       , metadata.getDataTypes().get(18));
1057         Assertions.assertEquals(ObservationType.RECEIVE_FREQ_3       , metadata.getDataTypes().get(19));
1058         Assertions.assertEquals(ObservationType.RECEIVE_FREQ_4       , metadata.getDataTypes().get(20));
1059         Assertions.assertEquals(ObservationType.RECEIVE_FREQ_5       , metadata.getDataTypes().get(21));
1060         Assertions.assertEquals(ObservationType.RECEIVE_FREQ         , metadata.getDataTypes().get(22));
1061         Assertions.assertEquals(ObservationType.TRANSMIT_FREQ_1      , metadata.getDataTypes().get(23));
1062         Assertions.assertEquals(ObservationType.TRANSMIT_FREQ_2      , metadata.getDataTypes().get(24));
1063         Assertions.assertEquals(ObservationType.TRANSMIT_FREQ_3      , metadata.getDataTypes().get(25));
1064         Assertions.assertEquals(ObservationType.TRANSMIT_FREQ_4      , metadata.getDataTypes().get(26));
1065         Assertions.assertEquals(ObservationType.TRANSMIT_FREQ_5      , metadata.getDataTypes().get(27));
1066         Assertions.assertEquals(ObservationType.TRANSMIT_FREQ_RATE_1 , metadata.getDataTypes().get(28));
1067         Assertions.assertEquals(ObservationType.TRANSMIT_FREQ_RATE_2 , metadata.getDataTypes().get(29));
1068         Assertions.assertEquals(ObservationType.TRANSMIT_FREQ_RATE_3 , metadata.getDataTypes().get(30));
1069         Assertions.assertEquals(ObservationType.TRANSMIT_FREQ_RATE_4 , metadata.getDataTypes().get(31));
1070         Assertions.assertEquals(ObservationType.TRANSMIT_FREQ_RATE_5 , metadata.getDataTypes().get(32));
1071         Assertions.assertEquals(ObservationType.DOR                  , metadata.getDataTypes().get(33));
1072         Assertions.assertEquals(ObservationType.VLBI_DELAY           , metadata.getDataTypes().get(34));
1073         Assertions.assertEquals(ObservationType.ANGLE_1              , metadata.getDataTypes().get(35));
1074         Assertions.assertEquals(ObservationType.ANGLE_2              , metadata.getDataTypes().get(36));
1075         Assertions.assertEquals(ObservationType.MAG                  , metadata.getDataTypes().get(37));
1076         Assertions.assertEquals(ObservationType.RCS                  , metadata.getDataTypes().get(38));
1077         Assertions.assertEquals(ObservationType.CLOCK_BIAS           , metadata.getDataTypes().get(39));
1078         Assertions.assertEquals(ObservationType.CLOCK_DRIFT          , metadata.getDataTypes().get(40));
1079         Assertions.assertEquals(ObservationType.STEC                 , metadata.getDataTypes().get(41));
1080         Assertions.assertEquals(ObservationType.TROPO_DRY            , metadata.getDataTypes().get(42));
1081         Assertions.assertEquals(ObservationType.TROPO_WET            , metadata.getDataTypes().get(43));
1082         Assertions.assertEquals(ObservationType.PRESSURE             , metadata.getDataTypes().get(44));
1083         Assertions.assertEquals(ObservationType.RHUMIDITY            , metadata.getDataTypes().get(45));
1084         Assertions.assertEquals(ObservationType.TEMPERATURE          , metadata.getDataTypes().get(46));
1085         Assertions.assertEquals("UTC", metadata.getTimeSystem().name());
1086         Assertions.assertEquals(new AbsoluteDate("2017-06-14T10:53:00.000", utc).durationFrom(metadata.getStartTime()), 0.0, 0.0);
1087         Assertions.assertEquals(new AbsoluteDate("2017-06-15T10:53:00.000", utc).durationFrom(metadata.getStopTime()), 0.0, 0.0);
1088         Assertions.assertEquals("DSS-25", metadata.getParticipants().get(1));
1089         Assertions.assertEquals("yyyy-nnnA", metadata.getParticipants().get(2));
1090         Assertions.assertEquals("P3", metadata.getParticipants().get(3));
1091         Assertions.assertEquals("P4", metadata.getParticipants().get(4));
1092         Assertions.assertEquals("P5", metadata.getParticipants().get(5));
1093         Assertions.assertEquals("S", metadata.getTransmitBand());
1094         Assertions.assertEquals("L", metadata.getReceiveBand());
1095         Assertions.assertEquals(240, metadata.getTurnaroundNumerator(), 0);
1096         Assertions.assertEquals(221, metadata.getTurnaroundDenominator(), 0);
1097         Assertions.assertEquals(TimetagReference.TRANSMIT, metadata.getTimetagRef());
1098         Assertions.assertEquals(1.0, metadata.getIntegrationInterval(), 0.0);
1099         Assertions.assertEquals(IntegrationReference.MIDDLE, metadata.getIntegrationRef());
1100         Assertions.assertEquals(32021035200.0, metadata.getFreqOffset(), 0.0);
1101         Assertions.assertEquals(RangeMode.COHERENT, metadata.getRangeMode());
1102         Assertions.assertEquals(32768.0, metadata.getRawRangeModulus(), 0.0);
1103         Assertions.assertEquals(RangeUnits.RU, metadata.getRangeUnits());
1104         Assertions.assertEquals(AngleType.RADEC, metadata.getAngleType());
1105         Assertions.assertEquals("EME2000", metadata.getReferenceFrame().getName());
1106         Assertions.assertEquals(CelestialBodyFrame.EME2000, metadata.getReferenceFrame().asCelestialBodyFrame());
1107         Assertions.assertEquals(FramesFactory.getEME2000(), metadata.getReferenceFrame().asFrame());
1108         Assertions.assertEquals("HERMITE", metadata.getInterpolationMethod());
1109         Assertions.assertEquals(5, metadata.getInterpolationDegree());
1110         Assertions.assertEquals(120000.0, metadata.getDopplerCountBias(), 1.0e-5);
1111         Assertions.assertEquals(1000.0, metadata.getDopplerCountScale(), 1.0e-10);
1112         Assertions.assertFalse(metadata.hasDopplerCountRollover());
1113         Assertions.assertEquals(0.000077, metadata.getTransmitDelays().get(1), 0.0);
1114         Assertions.assertEquals(0.000077, metadata.getTransmitDelays().get(2), 0.0);
1115         Assertions.assertEquals(0.000077, metadata.getTransmitDelays().get(3), 0.0);
1116         Assertions.assertEquals(0.000077, metadata.getTransmitDelays().get(4), 0.0);
1117         Assertions.assertEquals(0.000077, metadata.getTransmitDelays().get(5), 0.0);
1118         Assertions.assertEquals(0.000077, metadata.getReceiveDelays().get(1), 0.0);
1119         Assertions.assertEquals(0.000077, metadata.getReceiveDelays().get(2), 0.0);
1120         Assertions.assertEquals(0.000077, metadata.getReceiveDelays().get(3), 0.0);
1121         Assertions.assertEquals(0.000077, metadata.getReceiveDelays().get(4), 0.0);
1122         Assertions.assertEquals(0.000077, metadata.getReceiveDelays().get(5), 0.0);
1123         Assertions.assertEquals(DataQuality.RAW, metadata.getDataQuality());
1124         Assertions.assertEquals(FastMath.toRadians(1.0), metadata.getCorrectionAngle1(), 0.0);
1125         Assertions.assertEquals(FastMath.toRadians(2.0), metadata.getCorrectionAngle2(), 0.0);
1126         Assertions.assertEquals(3000.0, metadata.getCorrectionDoppler(), 0.0);
1127         Assertions.assertEquals(4.0, metadata.getCorrectionMagnitude(), 0.0);
1128         Assertions.assertEquals(5.0, metadata.getRawCorrectionRange(), 0.0);
1129         Assertions.assertEquals(6.0, metadata.getCorrectionRcs(), 0.0);
1130         Assertions.assertEquals(7.0, metadata.getCorrectionReceive(), 0.0);
1131         Assertions.assertEquals(8.0, metadata.getCorrectionTransmit(), 0.0);
1132         Assertions.assertEquals(FastMath.toRadians(9.0), metadata.getCorrectionAberrationYearly(), 0.0);
1133         Assertions.assertEquals(FastMath.toRadians(10.0), metadata.getCorrectionAberrationDiurnal(), 0.0);
1134         Assertions.assertEquals(CorrectionApplied.YES, metadata.getCorrectionsApplied());
1135 
1136         final List<String> metaDataComment = new ArrayList<String>();
1137         metaDataComment.add("All known meta-data keywords displayed");
1138         Assertions.assertEquals(metaDataComment, metadata.getComments());
1139 
1140         // Data
1141         final List<Observation> observations = file.getSegments().get(0).getData().getObservations();
1142 
1143         // Reference data
1144         final AbsoluteDate epoch = new AbsoluteDate("2017-06-14T10:53:00.000", utc);
1145         // Check consistency
1146         for (int i = 0; i < metadata.getDataTypes().size(); i++) {
1147             Assertions.assertEquals(metadata.getDataTypes().get(i), observations.get(i).getType());
1148             Assertions.assertEquals(epoch.shiftedBy((double) (i+1)).durationFrom(observations.get(i).getEpoch()), 0.0, 0.0);
1149             Assertions.assertEquals((double) (i+1), observations.get(i).getMeasurement(), 1.0e-12);
1150         }
1151 
1152         // Comment
1153         final List<String> dataComment = new ArrayList<String>();
1154         dataComment.add("Data Related Keywords");
1155         Assertions.assertEquals(dataComment, file.getSegments().get(0).getData().getComments());
1156     }
1157 
1158 }