1   /* Copyright 2002-2022 CS GROUP
2    * Licensed to CS GROUP (CS) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * CS licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.orekit.files.ccsds.ndm.odm.oem;
18  
19  import static org.junit.Assert.assertEquals;
20  
21  import java.io.ByteArrayInputStream;
22  import java.io.StringReader;
23  import java.nio.charset.StandardCharsets;
24  import java.util.ArrayList;
25  import java.util.Arrays;
26  import java.util.List;
27  
28  import org.hamcrest.CoreMatchers;
29  import org.hamcrest.MatcherAssert;
30  import org.hipparchus.geometry.euclidean.threed.Vector3D;
31  import org.junit.Before;
32  import org.junit.Test;
33  import org.orekit.Utils;
34  import org.orekit.bodies.CelestialBody;
35  import org.orekit.bodies.CelestialBodyFactory;
36  import org.orekit.bodies.GeodeticPoint;
37  import org.orekit.bodies.OneAxisEllipsoid;
38  import org.orekit.data.DataContext;
39  import org.orekit.data.DataSource;
40  import org.orekit.files.ccsds.definitions.CelestialBodyFrame;
41  import org.orekit.files.ccsds.definitions.CenterName;
42  import org.orekit.files.ccsds.definitions.FrameFacade;
43  import org.orekit.files.ccsds.definitions.ModifiedFrame;
44  import org.orekit.files.ccsds.definitions.TimeSystem;
45  import org.orekit.files.ccsds.ndm.ParsedUnitsBehavior;
46  import org.orekit.files.ccsds.ndm.ParserBuilder;
47  import org.orekit.files.ccsds.ndm.WriterBuilder;
48  import org.orekit.files.ccsds.section.Header;
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.TopocentricFrame;
54  import org.orekit.propagation.BoundedPropagator;
55  import org.orekit.time.AbsoluteDate;
56  import org.orekit.utils.Constants;
57  import org.orekit.utils.IERSConventions;
58  import org.orekit.utils.TimeStampedPVCoordinates;
59  
60  /**
61   * Check {@link StreamingOemWriter}.
62   *
63   * @author Evan Ward
64   */
65  public class StreamingOemWriterTest {
66      // As the default format for position is 3 digits after decimal point in km the max precision in m is 1
67      private static final double POSITION_PRECISION = 1; // in m
68      // As the default format for velocity is 5 digits after decimal point in km/s the max precision in m/s is 1e-2
69      private static final double VELOCITY_PRECISION = 1e-2; //in m/s
70  
71      /** Set Orekit data. */
72      @Before
73      public void setUp() {
74          Utils.setDataRoot("regular-data");
75      }
76  
77      /**
78       * Check guessing the frame center for some frames.
79       */
80      @Test
81      public void testGuessCenter() {
82          // action + verify
83          // check all CCSDS common center names
84          List<CenterName> centerNames = new ArrayList<>(Arrays.asList(CenterName.values()));
85          centerNames.remove(CenterName.EARTH_MOON);
86          for (CenterName centerName : centerNames) {
87              CelestialBody body = centerName.getCelestialBody();
88              String name = centerName.name().replace('_', ' ');
89              MatcherAssert.assertThat(CenterName.guessCenter(body.getInertiallyOrientedFrame()),
90                                       CoreMatchers.is(name));
91              MatcherAssert.assertThat(CenterName.guessCenter(body.getBodyOrientedFrame()),
92                                       CoreMatchers.is(name));
93          }
94          // Earth-Moon Barycenter is special
95          CelestialBody emb = CenterName.EARTH_MOON.getCelestialBody();
96          MatcherAssert.assertThat(CenterName.guessCenter(emb.getInertiallyOrientedFrame()),
97                                   CoreMatchers.is("EARTH-MOON BARYCENTER"));
98          MatcherAssert.assertThat(CenterName.guessCenter(emb.getBodyOrientedFrame()),
99                                   CoreMatchers.is("EARTH-MOON BARYCENTER"));
100         // check some special CCSDS frames
101         ModifiedFrame frame = new ModifiedFrame(FramesFactory.getEME2000(),
102                                                           CelestialBodyFrame.EME2000,
103                                                           CelestialBodyFactory.getMars(), "MARS");
104         MatcherAssert.assertThat(CenterName.guessCenter(frame), CoreMatchers.is("MARS"));
105 
106         // check unknown frame
107         Frame topo = new TopocentricFrame(new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
108                                                                Constants.WGS84_EARTH_FLATTENING,
109                                                                FramesFactory.getITRF(IERSConventions.IERS_2010, true)),
110                                           new GeodeticPoint(1.2, 2.3, 45.6),
111                                           "dummy");
112         MatcherAssert.assertThat(CenterName.guessCenter(topo), CoreMatchers.is("UNKNOWN"));
113     }
114 
115 
116     /**
117      * Check reading and writing an OEM both with and without using the step handler
118      * methods.
119      *
120      * @throws Exception on error.
121      */
122     @Test
123     public void testWriteOemStepHandler() throws Exception {
124         // setup
125         List<String> files =
126                 Arrays.asList("/ccsds/odm/oem/OEMExample5.txt", "/ccsds/odm/oem/OEMExample4.txt");
127         for (final String ex : files) {
128             final DataSource source0 =  new DataSource(ex, () -> getClass().getResourceAsStream(ex));
129             final OemParser parser  = new ParserBuilder().withMu(CelestialBodyFactory.getEarth().getGM()).buildOemParser();
130             Oem oem = parser.parseMessage(source0);
131 
132             OemSatelliteEphemeris satellite = oem.getSatellites().values().iterator().next();
133             OemSegment ephemerisBlock = satellite.getSegments().get(0);
134             double step = ephemerisBlock.
135                           getMetadata().
136                           getStopTime().
137                           durationFrom(ephemerisBlock.getMetadata().getStartTime()) /
138                           (ephemerisBlock.getCoordinates().size() - 1);
139             String originator = oem.getHeader().getOriginator();
140             OemSegment block = oem.getSegments().get(0);
141             String objectName = block.getMetadata().getObjectName();
142             String objectID = block.getMetadata().getObjectID();
143 
144             Header header = new Header(3.0);
145             header.setOriginator(originator);
146             OemMetadata metadata = new OemMetadata(1);
147             metadata.setObjectName(objectName);
148             metadata.setObjectID(objectID);
149             metadata.setTimeSystem(TimeSystem.UTC);
150             metadata.setCenter(ephemerisBlock.getMetadata().getCenter());
151             metadata.setReferenceFrame(FrameFacade.map(FramesFactory.getEME2000())); // will be overwritten
152             metadata.setStartTime(AbsoluteDate.J2000_EPOCH.shiftedBy(80 * Constants.JULIAN_CENTURY));
153             metadata.setStopTime(metadata.getStartTime().shiftedBy(Constants.JULIAN_YEAR));
154 
155 
156             // check using the Propagator / StepHandler interface
157             final StringBuilder buffer1 = new StringBuilder();
158             StreamingOemWriter writer = new StreamingOemWriter(new KvnGenerator(buffer1, OemWriter.KVN_PADDING_WIDTH, "some-name", 60),
159                                                                new WriterBuilder().buildOemWriter(),
160                                                                header, metadata);
161             BoundedPropagator propagator = satellite.getPropagator();
162             propagator.setStepHandler(step, writer.newSegment());
163             propagator.propagate(propagator.getMinDate(), propagator.getMaxDate());
164             writer.close();
165 
166             // verify
167             final DataSource source1 = new DataSource("buffer",
168                                                     () -> new ByteArrayInputStream(buffer1.toString().getBytes(StandardCharsets.UTF_8)));
169             Oem generatedOem = new ParserBuilder().
170                                        withConventions(IERSConventions.IERS_2010).
171                                        withSimpleEOP(true).
172                                        withDataContext(DataContext.getDefault()).
173                                        withMu(CelestialBodyFactory.getEarth().getGM()).
174                                        withDefaultInterpolationDegree(1).
175                                        buildOemParser().
176                                        parseMessage(source1);
177             compareOems(oem, generatedOem, POSITION_PRECISION, VELOCITY_PRECISION);
178 
179             // check calling the methods directly
180             final StringBuilder buffer2 = new StringBuilder();
181             OemWriter oemWriter = new WriterBuilder().buildOemWriter();
182             try (Generator generator = new KvnGenerator(buffer2, OemWriter.KVN_PADDING_WIDTH, "another-name", 60)) {
183                 oemWriter.writeHeader(generator, header);
184                 metadata.setObjectName(objectName);
185                 metadata.setStartTime(block.getStart());
186                 metadata.setStopTime(block.getStop());
187                 final Frame      stateFrame = satellite.getPropagator().getFrame();
188                 metadata.setReferenceFrame(FrameFacade.map(stateFrame));
189                 oemWriter.writeMetadata(generator, metadata);
190                 for (TimeStampedPVCoordinates coordinate : block.getCoordinates()) {
191                     oemWriter.writeOrbitEphemerisLine(generator, metadata, coordinate, true);
192                 }
193             }
194 
195             // verify
196             final DataSource source2 = new DataSource("buffer",
197                                                     () -> new ByteArrayInputStream(buffer2.toString().getBytes(StandardCharsets.UTF_8)));
198             generatedOem = new ParserBuilder().
199                                withConventions(IERSConventions.IERS_2010).
200                                withSimpleEOP(true).
201                                withDataContext(DataContext.getDefault()).
202                                withMu(CelestialBodyFactory.getEarth().getGM()).
203                                withDefaultInterpolationDegree(1).
204                                withParsedUnitsBehavior(ParsedUnitsBehavior.STRICT_COMPLIANCE).
205                                buildOemParser().
206                                parseMessage(source2);
207             compareOems(oem, generatedOem, POSITION_PRECISION, VELOCITY_PRECISION);
208 
209         }
210 
211     }
212 
213     @Test
214     public void testWriteOemEcfNoInterpolation() {
215         // setup
216         String path = "/ccsds/odm/oem/OEMExample5.txt";
217         DataSource source = new DataSource(path, () -> getClass().getResourceAsStream(path));
218         final OemParser oemParser = new ParserBuilder().buildOemParser();
219         final Oem original = oemParser.parse(source);
220         final OemSatelliteEphemeris originalEphem =
221                 original.getSatellites().values().iterator().next();
222         final BoundedPropagator propagator = originalEphem.getPropagator();
223         StringBuilder buffer = new StringBuilder();
224         Header header = original.getHeader();
225         OemMetadata metadata = original.getSegments().get(0).getMetadata();
226         metadata.setTimeSystem(TimeSystem.UTC);
227         metadata.setReferenceFrame(FrameFacade.map(FramesFactory.getITRF(IERSConventions.IERS_2010, true)));
228         metadata.setInterpolationMethod(null);
229         metadata.setInterpolationDegree(-1);
230 
231         // action
232         StreamingOemWriter writer = new StreamingOemWriter(
233                 new KvnGenerator(buffer, OemWriter.KVN_PADDING_WIDTH, "out", 0),
234                 new WriterBuilder().buildOemWriter(),
235                 header,
236                 metadata,
237                 false,
238                 false);
239         propagator.setStepHandler(30 * 60, writer.newSegment());
240         propagator.propagate(propagator.getMinDate(), propagator.getMaxDate());
241 
242         // verify
243         String actualText = buffer.toString();
244         String expectedPath = "/ccsds/odm/oem/OEMExample5ITRF.txt";
245         Oem expected = oemParser.parse(
246                 new DataSource(expectedPath, () -> getClass().getResourceAsStream(expectedPath)));
247         Oem actual = oemParser.parse(
248                 new DataSource("mem", () -> new StringReader(actualText)));
249 
250         compareOems(expected, actual, 1e-6, 1e-9);
251         MatcherAssert.assertThat(
252                 actualText,
253                 CoreMatchers.not(CoreMatchers.containsString("INTERPOLATION_DEGREE")));
254         // check no acceleration
255         MatcherAssert.assertThat(
256                 actualText,
257                 CoreMatchers.containsString(
258                         "\n2017-04-11T22:31:43.121856 -2757.3016318893897 -4173.479601381253 4566.01849801963 6.625901653953951 -1.011817208875361 3.0698336591568833\n"));
259     }
260 
261     private static void compareOemEphemerisBlocks(OemSegment block1,
262                                                   OemSegment block2,
263                                                   double p_tol,
264                                                   double v_tol) {
265         compareOemEphemerisBlocksMetadata(block1.getMetadata(), block2.getMetadata());
266         assertEquals(block1.getStart(), block2.getStart());
267         assertEquals(block1.getStop(), block2.getStop());
268         assertEquals(block1.getData().getEphemeridesDataLines().size(), block2.getData().getEphemeridesDataLines().size());
269         for (int i = 0; i < block1.getData().getEphemeridesDataLines().size(); i++) {
270             TimeStampedPVCoordinates c1 = block1.getData().getEphemeridesDataLines().get(i);
271             TimeStampedPVCoordinates c2 = block2.getData().getEphemeridesDataLines().get(i);
272             assertEquals("" + i, c1.getDate(), c2.getDate());
273             assertEquals(c1.getPosition() + " -> " + c2.getPosition(), 0.0,
274                     Vector3D.distance(c1.getPosition(), c2.getPosition()), p_tol);
275             assertEquals(c1.getVelocity() + " -> " + c2.getVelocity(), 0.0,
276                     Vector3D.distance(c1.getVelocity(), c2.getVelocity()), v_tol);
277         }
278 
279     }
280 
281     private static void compareOemEphemerisBlocksMetadata(OemMetadata meta1, OemMetadata meta2) {
282         assertEquals(meta1.getObjectID(),                               meta2.getObjectID());
283         assertEquals(meta1.getObjectName(),                             meta2.getObjectName());
284         assertEquals(meta1.getCenter().getName(),                       meta2.getCenter().getName());
285         assertEquals(meta1.getReferenceFrame().asFrame(),               meta2.getReferenceFrame().asFrame());
286         assertEquals(meta1.getReferenceFrame().asCelestialBodyFrame(),  meta2.getReferenceFrame().asCelestialBodyFrame());
287         assertEquals(meta1.getReferenceFrame().asOrbitRelativeFrame(),  meta2.getReferenceFrame().asOrbitRelativeFrame());
288         assertEquals(meta1.getReferenceFrame().asSpacecraftBodyFrame(), meta2.getReferenceFrame().asSpacecraftBodyFrame());
289         assertEquals(meta1.getTimeSystem().name(),    meta2.getTimeSystem().name());
290     }
291 
292     static void compareOems(Oem file1, Oem file2, double p_tol, double v_tol) {
293         assertEquals(file1.getHeader().getOriginator(), file2.getHeader().getOriginator());
294         assertEquals(file1.getSegments().size(), file2.getSegments().size());
295         for (int i = 0; i < file1.getSegments().size(); i++) {
296             compareOemEphemerisBlocks(file1.getSegments().get(i), file2.getSegments().get(i), p_tol, v_tol);
297         }
298     }
299 
300 }