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