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