1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.files.ccsds.ndm.adm.aem;
18
19 import java.io.BufferedReader;
20 import java.io.BufferedWriter;
21 import java.io.ByteArrayInputStream;
22 import java.io.CharArrayWriter;
23 import java.io.File;
24 import java.io.IOException;
25 import java.io.InputStreamReader;
26 import java.nio.charset.StandardCharsets;
27 import java.nio.file.Path;
28 import java.util.ArrayList;
29 import java.util.Collections;
30 import java.util.HashMap;
31 import java.util.List;
32 import java.util.Map;
33
34 import org.hipparchus.geometry.euclidean.threed.Rotation;
35 import org.hipparchus.geometry.euclidean.threed.Vector3D;
36 import org.junit.jupiter.api.Assertions;
37 import org.junit.jupiter.api.BeforeEach;
38 import org.junit.jupiter.api.Test;
39 import org.junit.jupiter.api.io.TempDir;
40 import org.orekit.Utils;
41 import org.orekit.data.DataContext;
42 import org.orekit.data.DataSource;
43 import org.orekit.errors.OrekitIllegalArgumentException;
44 import org.orekit.errors.OrekitMessages;
45 import org.orekit.files.ccsds.definitions.CelestialBodyFrame;
46 import org.orekit.files.ccsds.definitions.FrameFacade;
47 import org.orekit.files.ccsds.definitions.SpacecraftBodyFrame;
48 import org.orekit.files.ccsds.definitions.TimeSystem;
49 import org.orekit.files.ccsds.ndm.ParserBuilder;
50 import org.orekit.files.ccsds.ndm.WriterBuilder;
51 import org.orekit.files.ccsds.ndm.adm.AdmHeader;
52 import org.orekit.files.ccsds.ndm.adm.AttitudeType;
53 import org.orekit.files.ccsds.utils.FileFormat;
54 import org.orekit.files.ccsds.utils.generation.KvnGenerator;
55 import org.orekit.files.general.AttitudeEphemerisFile;
56 import org.orekit.frames.Frame;
57 import org.orekit.frames.FramesFactory;
58 import org.orekit.time.AbsoluteDate;
59 import org.orekit.utils.Constants;
60 import org.orekit.utils.IERSConventions;
61 import org.orekit.utils.TimeStampedAngularCoordinates;
62
63 public class AttitudeWriterTest {
64
65
66 private static final double QUATERNION_PRECISION = 1e-5;
67 private static final double DATE_PRECISION = 1e-3;
68
69 @TempDir
70 public Path temporaryFolderPath;
71
72 @BeforeEach
73 public void setUp() throws Exception {
74 Utils.setDataRoot("regular-data");
75 }
76
77 @Test
78 public void testAEMWriter() {
79 Assertions.assertNotNull(new WriterBuilder().buildAemWriter());
80 }
81
82 @Test
83 public void testWriteAEM1() throws IOException {
84 final String ex = "/ccsds/adm/aem/AEMExample01.txt";
85 final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
86 final Aem aem = new ParserBuilder().buildAemParser().parseMessage(source);
87
88 AdmHeader header = new AdmHeader();
89 header.setFormatVersion(aem.getHeader().getFormatVersion());
90 header.setCreationDate(aem.getHeader().getCreationDate());
91 header.setOriginator(aem.getHeader().getOriginator());
92
93 final AemSegment s0 = aem.getSegments().get(0);
94 AemMetadata metadata = new AemMetadata(s0.getInterpolationSamples() - 1);
95 metadata.setObjectName(s0.getMetadata().getObjectName());
96 metadata.setObjectID(s0.getMetadata().getObjectID());
97 metadata.getEndpoints().setFrameA(s0.getMetadata().getEndpoints().getFrameA());
98 metadata.getEndpoints().setFrameB(s0.getMetadata().getEndpoints().getFrameB());
99 metadata.getEndpoints().setA2b(s0.getMetadata().getEndpoints().isA2b());
100 metadata.setTimeSystem(s0.getMetadata().getTimeSystem());
101 metadata.setStartTime(s0.getMetadata().getStart());
102 metadata.setStopTime(s0.getMetadata().getStop());
103 metadata.setAttitudeType(s0.getMetadata().getAttitudeType());
104 metadata.setIsFirst(s0.getMetadata().isFirst());
105 metadata.setCenter(s0.getMetadata().getCenter());
106 metadata.setInterpolationMethod(s0.getMetadata().getInterpolationMethod());
107 AemWriter writer = new WriterBuilder().
108 withConventions(IERSConventions.IERS_2010).
109 withDataContext(DataContext.getDefault()).
110 buildAemWriter();
111 final CharArrayWriter caw = new CharArrayWriter();
112 writer.writeMessage(new KvnGenerator(caw, 0, "", Constants.JULIAN_DAY, 60), aem);
113 final byte[] bytes = caw.toString().getBytes(StandardCharsets.UTF_8);
114
115 final Aem generatedOem = new ParserBuilder().buildAemParser().
116 parseMessage(new DataSource("", () -> new ByteArrayInputStream(bytes)));
117 compareAems(aem, generatedOem);
118 }
119
120 @Test
121 public void testUnfoundSpaceId() throws IOException {
122 final String ex = "/ccsds/adm/aem/AEMExample01.txt";
123 final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
124 final Aem aem = new ParserBuilder().buildAemParser().parseMessage(source);
125
126 AemMetadata metadata = dummyMetadata();
127 metadata.setObjectID("12345");
128 AttitudeWriter writer = new AttitudeWriter(new WriterBuilder().buildAemWriter(), null, metadata,
129 FileFormat.KVN, "", Constants.JULIAN_DAY, 60);
130 try {
131 writer.write(new CharArrayWriter(), aem);
132 Assertions.fail("an exception should have been thrown");
133 } catch (OrekitIllegalArgumentException oiae) {
134 Assertions.assertEquals(OrekitMessages.VALUE_NOT_FOUND, oiae.getSpecifier());
135 Assertions.assertEquals(metadata.getObjectID(), oiae.getParts()[0]);
136 }
137 }
138
139 @Test
140 public void testNullFile() throws IOException {
141 final String ex = "/ccsds/adm/aem/AEMExample01.txt";
142 final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
143 final Aem aem = new ParserBuilder().buildAemParser().parseMessage(source);
144 AttitudeWriter writer = new AttitudeWriter(new WriterBuilder().
145 withConventions(aem.getConventions()).
146 withDataContext(aem.getDataContext()).
147 buildAemWriter(),
148 aem.getHeader(),
149 aem.getSegments().get(0).getMetadata(),
150 FileFormat.KVN,
151 "dummy", Constants.JULIAN_DAY, 0);
152 try {
153 writer.write((BufferedWriter) null, aem);
154 Assertions.fail("an exception should have been thrown");
155 } catch (OrekitIllegalArgumentException oiae) {
156 Assertions.assertEquals(OrekitMessages.NULL_ARGUMENT, oiae.getSpecifier());
157 Assertions.assertEquals("writer", oiae.getParts()[0]);
158 }
159 }
160
161 @Test
162 public void testNullEphemeris() throws IOException {
163 AdmHeader header = new AdmHeader();
164 header.setOriginator("NASA/JPL");
165 AemMetadata metadata = dummyMetadata();
166 metadata.setObjectID("1996-062A");
167 metadata.setObjectName("MARS GLOBAL SURVEYOR");
168 AttitudeWriter writer = new AttitudeWriter(new WriterBuilder().buildAemWriter(),
169 header, metadata, FileFormat.KVN, "TestNullEphemeris.aem",
170 Constants.JULIAN_DAY, 0);
171 CharArrayWriter caw = new CharArrayWriter();
172 writer.write(caw, null);
173 Assertions.assertEquals(0, caw.size());
174 }
175
176 @Test
177 public void testUnisatelliteFileWithDefault() throws IOException {
178 final String ex = "/ccsds/adm/aem/AEMExample01.txt";
179 final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
180 final Aem aem = new ParserBuilder().buildAemParser().parseMessage(source);
181
182 final File temp = temporaryFolderPath.resolve("writeAEMExample01.xml").toFile();
183 AttitudeWriter writer = new AttitudeWriter(new WriterBuilder().buildAemWriter(),
184 aem.getHeader(), aem.getSegments().get(0).getMetadata(),
185 FileFormat.XML, temp.getName(), Constants.JULIAN_DAY, 1);
186 writer.write(temp.getAbsolutePath(), aem);
187 final Aem generatedAem = new ParserBuilder().buildAemParser().parseMessage(new DataSource(temp));
188 Assertions.assertEquals(aem.getSegments().get(0).getMetadata().getObjectID(),
189 generatedAem.getSegments().get(0).getMetadata().getObjectID());
190 }
191
192 @Test
193 public void testMultisatelliteFile() throws IOException {
194
195 final DataContext context = DataContext.getDefault();
196 final String id1 = "1999-012A";
197 final String id2 = "1999-012B";
198 StandAloneEphemerisFile file = new StandAloneEphemerisFile();
199 file.generate(id1, id1 + "-name", AttitudeType.QUATERNION_ANGVEL,
200 context.getFrames().getEME2000(),
201 new TimeStampedAngularCoordinates(AbsoluteDate.GALILEO_EPOCH,
202 Rotation.IDENTITY,
203 new Vector3D(0.000, 0.010, 0.000),
204 new Vector3D(0.000, 0.000, 0.001)),
205 900.0, 60.0);
206 file.generate(id2, id2 + "-name", AttitudeType.QUATERNION_ANGVEL,
207 context.getFrames().getEME2000(),
208 new TimeStampedAngularCoordinates(AbsoluteDate.GALILEO_EPOCH,
209 Rotation.IDENTITY,
210 new Vector3D(0.000, -0.010, 0.000),
211 new Vector3D(0.000, 0.000, 0.003)),
212 600.0, 10.0);
213
214 AemMetadata metadata = dummyMetadata();
215 metadata.setObjectID(id2);
216 AdmHeader header = new AdmHeader();
217 header.setFormatVersion(1.0);
218 AttitudeWriter writer = new AttitudeWriter(new WriterBuilder().buildAemWriter(),
219 header, metadata, FileFormat.KVN, "",
220 Constants.JULIAN_DAY, 60);
221 final CharArrayWriter caw = new CharArrayWriter();
222 writer.write(caw, file);
223 final byte[] bytes = caw.toString().getBytes(StandardCharsets.UTF_8);
224
225 int count = 0;
226 try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
227 InputStreamReader isr = new InputStreamReader(bais, StandardCharsets.UTF_8);
228 BufferedReader br = new BufferedReader(isr)) {
229 for (String line = br.readLine(); line != null; line = br.readLine()) {
230 ++count;
231 }
232 }
233 Assertions.assertEquals(81, count);
234
235 }
236
237 @Test
238 public void testIssue723() throws IOException {
239 final String ex = "/ccsds/adm/aem/AEMExample02.txt";
240 final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
241 final Aem aem = new ParserBuilder().buildAemParser().parseMessage(source);
242
243 AttitudeWriter writer = new AttitudeWriter(new WriterBuilder().buildAemWriter(),
244 aem.getHeader(), aem.getSegments().get(0).getMetadata(),
245 FileFormat.KVN, "TestAEMIssue723.aem",
246 Constants.JULIAN_DAY, 0);
247 final CharArrayWriter caw = new CharArrayWriter();
248 writer.write(caw, aem);
249 final byte[] bytes = caw.toString().getBytes(StandardCharsets.UTF_8);
250
251 final Aem generatedAem = new ParserBuilder().buildAemParser().
252 parseMessage(new DataSource("", () -> new ByteArrayInputStream(bytes)));
253 Assertions.assertEquals(aem.getHeader().getComments().get(0), generatedAem.getHeader().getComments().get(0));
254 }
255
256 @Test
257 public void testWriteAemFormat() throws IOException {
258
259 String exampleFile = "/ccsds/adm/aem/AEMExample07.txt";
260 final DataSource source = new DataSource(exampleFile, () -> getClass().getResourceAsStream(exampleFile));
261 final Aem aem = new ParserBuilder().buildAemParser().parseMessage(source);
262
263 AemWriter writer = new WriterBuilder().buildAemWriter();
264 final CharArrayWriter caw = new CharArrayWriter();
265 writer.writeMessage(new KvnGenerator(caw, 0, "", Constants.JULIAN_DAY, 60), aem);
266
267 String[] lines2 = caw.toString().split("\n");
268
269 Assertions.assertEquals("2002-12-18T12:00:00.331 0.5674807981623039 0.031460044248583355 0.4568906426171408 0.6842709624277855", lines2[26]);
270 Assertions.assertEquals("2002-12-18T12:01:00.331 0.4231908397172568 -0.4569709067454213 0.23784047193542462 0.7453314789254544", lines2[27]);
271 Assertions.assertEquals("2002-12-18T12:02:00.331 -0.8453188238242068 0.2697396246845473 -0.0653199091139417 0.4565193647993977", lines2[28]);
272 }
273
274 private static void compareAemAttitudeBlocks(AemSegment segment1, AemSegment segment2) {
275
276
277 AemMetadata meta1 = segment1.getMetadata();
278 AemMetadata meta2 = segment2.getMetadata();
279 Assertions.assertEquals(meta1.getObjectID(), meta2.getObjectID());
280 Assertions.assertEquals(meta1.getObjectName(), meta2.getObjectName());
281 Assertions.assertEquals(meta1.getCenter().getName(), meta2.getCenter().getName());
282 Assertions.assertEquals(meta1.getTimeSystem().name(), meta2.getTimeSystem().name());
283 Assertions.assertEquals(meta1.getLaunchYear(), meta2.getLaunchYear());
284 Assertions.assertEquals(meta1.getLaunchNumber(), meta2.getLaunchNumber());
285 Assertions.assertEquals(meta1.getLaunchPiece(), meta2.getLaunchPiece());
286 Assertions.assertEquals(meta1.getHasCreatableBody(), meta2.getHasCreatableBody());
287 Assertions.assertEquals(meta1.getInterpolationDegree(), meta2.getInterpolationDegree());
288
289
290 Assertions.assertEquals(0.0, segment1.getStart().durationFrom(segment2.getStart()), DATE_PRECISION);
291 Assertions.assertEquals(0.0, segment1.getStop().durationFrom(segment2.getStop()), DATE_PRECISION);
292 Assertions.assertEquals(segment1.getInterpolationMethod(), segment2.getInterpolationMethod());
293 Assertions.assertEquals(segment1.getAngularCoordinates().size(), segment2.getAngularCoordinates().size());
294 for (int i = 0; i < segment1.getAngularCoordinates().size(); i++) {
295 TimeStampedAngularCoordinates c1 = segment1.getAngularCoordinates().get(i);
296 Rotation rot1 = c1.getRotation();
297 TimeStampedAngularCoordinates c2 = segment2.getAngularCoordinates().get(i);
298 Rotation rot2 = c2.getRotation();
299 Assertions.assertEquals(0.0, c1.getDate().durationFrom(c2.getDate()), DATE_PRECISION);
300 Assertions.assertEquals(rot1.getQ0(), rot2.getQ0(), QUATERNION_PRECISION);
301 Assertions.assertEquals(rot1.getQ1(), rot2.getQ1(), QUATERNION_PRECISION);
302 Assertions.assertEquals(rot1.getQ2(), rot2.getQ2(), QUATERNION_PRECISION);
303 Assertions.assertEquals(rot1.getQ3(), rot2.getQ3(), QUATERNION_PRECISION);
304 }
305 }
306
307 static void compareAems(Aem file1, Aem file2) {
308 Assertions.assertEquals(file1.getHeader().getOriginator(), file2.getHeader().getOriginator());
309 Assertions.assertEquals(file1.getSegments().size(), file2.getSegments().size());
310 for (int i = 0; i < file1.getSegments().size(); i++) {
311 compareAemAttitudeBlocks(file1.getSegments().get(i), file2.getSegments().get(i));
312 }
313 }
314
315 private class StandAloneEphemerisFile
316 implements AttitudeEphemerisFile<TimeStampedAngularCoordinates, AemSegment> {
317 private final Map<String, AemSatelliteEphemeris> satEphem;
318
319 public StandAloneEphemerisFile() {
320 this.satEphem = new HashMap<>();
321 }
322
323 private void generate(final String objectID, final String objectName,
324 final AttitudeType type, final Frame referenceFrame,
325 final TimeStampedAngularCoordinates ac0,
326 final double duration, final double step) {
327
328 AemMetadata metadata = dummyMetadata();
329 metadata.addComment("metadata for " + objectName);
330 metadata.setObjectID(objectID);
331 metadata.setObjectName(objectName);
332 metadata.getEndpoints().setFrameA(FrameFacade.map(referenceFrame));
333 metadata.setAttitudeType(type);
334 metadata.setStartTime(ac0.getDate());
335 metadata.setStopTime(ac0.getDate().shiftedBy(duration));
336 metadata.setUseableStartTime(metadata.getStartTime().shiftedBy(step));
337 metadata.setUseableStartTime(metadata.getStopTime().shiftedBy(-step));
338
339 AemData data = new AemData();
340 data.addComment("generated data for " + objectName);
341 data.addComment("duration was set to " + duration + " s");
342 data.addComment("step was set to " + step + " s");
343 for (double dt = 0; dt < duration; dt += step) {
344 data.addData(ac0.shiftedBy(dt));
345 }
346
347 if (!satEphem.containsKey(objectID)) {
348 satEphem.put(objectID, new AemSatelliteEphemeris(objectID, Collections.emptyList()));
349 }
350
351 List<AemSegment> segments = new ArrayList<>(satEphem.get(objectID).getSegments());
352 segments.add(new AemSegment(metadata, data));
353 satEphem.put(objectID, new AemSatelliteEphemeris(objectID, segments));
354
355 }
356
357 @Override
358 public Map<String, AemSatelliteEphemeris> getSatellites() {
359 return satEphem;
360 }
361
362 }
363
364 private AemMetadata dummyMetadata() {
365 AemMetadata metadata = new AemMetadata(4);
366 metadata.setTimeSystem(TimeSystem.TT);
367 metadata.setObjectID("9999-999ZZZ");
368 metadata.setObjectName("transgalactic");
369 metadata.getEndpoints().setFrameA(new FrameFacade(FramesFactory.getGCRF(), CelestialBodyFrame.GCRF,
370 null, null, "GCRF"));
371 metadata.getEndpoints().setFrameB(new FrameFacade(null, null, null,
372 new SpacecraftBodyFrame(SpacecraftBodyFrame.BaseEquipment.GYRO_FRAME, "1"),
373 "GYRO 1"));
374 metadata.getEndpoints().setA2b(true);
375 metadata.setStartTime(AbsoluteDate.J2000_EPOCH.shiftedBy(80 * Constants.JULIAN_CENTURY));
376 metadata.setStopTime(metadata.getStartTime().shiftedBy(Constants.JULIAN_YEAR));
377 metadata.setAttitudeType(AttitudeType.QUATERNION_DERIVATIVE);
378 metadata.setIsFirst(true);
379 return metadata;
380 }
381
382 }