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