1   /* Copyright 2002-2024 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.general;
18  
19  import org.hipparchus.geometry.euclidean.threed.Vector3D;
20  import org.hipparchus.util.FastMath;
21  import org.junit.jupiter.api.Assertions;
22  import org.junit.jupiter.api.BeforeEach;
23  import org.junit.jupiter.api.Test;
24  import org.orekit.Utils;
25  import org.orekit.attitudes.FrameAlignedProvider;
26  import org.orekit.bodies.CelestialBody;
27  import org.orekit.bodies.CelestialBodyFactory;
28  import org.orekit.bodies.GeodeticPoint;
29  import org.orekit.bodies.OneAxisEllipsoid;
30  import org.orekit.data.DataSource;
31  import org.orekit.errors.OrekitException;
32  import org.orekit.files.ccsds.definitions.BodyFacade;
33  import org.orekit.files.ccsds.definitions.FrameFacade;
34  import org.orekit.files.ccsds.definitions.TimeSystem;
35  import org.orekit.files.ccsds.ndm.ParserBuilder;
36  import org.orekit.files.ccsds.ndm.WriterBuilder;
37  import org.orekit.files.ccsds.ndm.odm.oem.EphemerisOemWriter;
38  import org.orekit.files.ccsds.ndm.odm.oem.OemMetadata;
39  import org.orekit.files.ccsds.ndm.odm.oem.OemParser;
40  import org.orekit.files.ccsds.ndm.odm.oem.OemSegment;
41  import org.orekit.files.ccsds.utils.FileFormat;
42  import org.orekit.files.general.EphemerisFile.EphemerisSegment;
43  import org.orekit.files.general.OrekitEphemerisFile.OrekitSatelliteEphemeris;
44  import org.orekit.frames.Frame;
45  import org.orekit.frames.FramesFactory;
46  import org.orekit.frames.TopocentricFrame;
47  import org.orekit.orbits.CartesianOrbit;
48  import org.orekit.orbits.KeplerianOrbit;
49  import org.orekit.orbits.PositionAngleType;
50  import org.orekit.propagation.Propagator;
51  import org.orekit.propagation.SpacecraftState;
52  import org.orekit.propagation.SpacecraftStateInterpolator;
53  import org.orekit.propagation.analytical.Ephemeris;
54  import org.orekit.propagation.analytical.KeplerianPropagator;
55  import org.orekit.propagation.events.ElevationDetector;
56  import org.orekit.propagation.events.EventsLogger;
57  import org.orekit.propagation.events.EventsLogger.LoggedEvent;
58  import org.orekit.time.AbsoluteDate;
59  import org.orekit.time.TimeInterpolator;
60  import org.orekit.utils.CartesianDerivativesFilter;
61  import org.orekit.utils.Constants;
62  import org.orekit.utils.IERSConventions;
63  import org.orekit.utils.TimeStampedPVCoordinates;
64  
65  import java.io.IOException;
66  import java.nio.file.Files;
67  import java.nio.file.Paths;
68  import java.util.ArrayList;
69  import java.util.List;
70  
71  public class OrekitEphemerisFileTest {
72  
73      @BeforeEach
74      public void setUp() throws Exception {
75          Utils.setDataRoot("regular-data");
76      }
77  
78      @Test
79      public void testOrekitEphemerisFile() {
80          Assertions.assertNotNull(new OrekitEphemerisFile());
81      }
82  
83      @Test
84      public void testGetSatellites() {
85          final String id1 = "ID1";
86          final String id2 = "ID2";
87          OrekitEphemerisFile file = new OrekitEphemerisFile();
88          OrekitSatelliteEphemeris ephem1 = file.addSatellite(id1);
89          Assertions.assertNotNull(ephem1);
90          OrekitSatelliteEphemeris ephem2 = file.addSatellite(id2);
91          Assertions.assertNotNull(ephem2);
92      }
93  
94      @Test
95      public void testWritingToOEMKvn() throws IOException {
96          doTestWritingToOEM(FileFormat.KVN);
97      }
98  
99      @Test
100     public void testWritingToOEMXml() throws IOException {
101         doTestWritingToOEM(FileFormat.XML);
102     }
103 
104     private void doTestWritingToOEM(final FileFormat fileFormat) throws IOException {
105         final double muTolerance = 1e-12;
106         // As the default format for position is 3 digits after decimal point in km the max precision in m is 1
107         final double positionTolerance = 1.;
108         // As the default format for velocity is 5 digits after decimal point in km/s the max precision in m/s is 1e-2
109         final double velocityTolerance = 1e-2;
110         final String satId = "SATELLITE1";
111         final double sma = 10000000;
112         final double inc = Math.toRadians(45.0);
113         final double ecc = 0.001;
114         final double raan = 0.0;
115         final double pa = 0.0;
116         final double ta = 0.0;
117         final AbsoluteDate date = new AbsoluteDate();
118         final Frame frame = FramesFactory.getGCRF();
119         final CelestialBody body = CelestialBodyFactory.getEarth();
120         final double mu = body.getGM();
121         KeplerianOrbit initialOrbit = new KeplerianOrbit(sma, ecc, inc, pa, raan, ta, PositionAngleType.TRUE, frame, date,
122                 mu);
123         KeplerianPropagator propagator = new KeplerianPropagator(initialOrbit);
124 
125         final double propagationDurationSeconds = 86400.0;
126         final double stepSizeSeconds = 60.0;
127         List<SpacecraftState> states = new ArrayList<SpacecraftState>();
128 
129         for (double dt = 0.0; dt < propagationDurationSeconds; dt += stepSizeSeconds) {
130             states.add(propagator.propagate(date.shiftedBy(dt)));
131         }
132 
133         OrekitEphemerisFile ephemerisFile = new OrekitEphemerisFile();
134         OrekitSatelliteEphemeris satellite = ephemerisFile.addSatellite(satId);
135         satellite.addNewSegment(states);
136         Assertions.assertEquals(satId, satellite.getId());
137         Assertions.assertEquals(body.getGM(), satellite.getMu(), muTolerance);
138         Assertions.assertEquals(0.0, states.get(0).getDate().durationFrom(satellite.getStart()), 1.0e-15);
139         Assertions.assertEquals(0.0, states.get(states.size() - 1).getDate().durationFrom(satellite.getStop()), 1.0e-15);
140         Assertions.assertEquals(CartesianDerivativesFilter.USE_PV,
141                      satellite.getSegments().get(0).getAvailableDerivatives());
142         Assertions.assertEquals("GCRF",
143                      satellite.getSegments().get(0).getFrame().getName());
144         Assertions.assertEquals(body.getGM(),
145                      satellite.getSegments().get(0).getMu(), muTolerance);
146 
147         String tempOem = Files.createTempFile("OrekitEphemerisFileTest", ".oem").toString();
148         OemMetadata template = new OemMetadata(2);
149         template.setTimeSystem(TimeSystem.UTC);
150         template.setObjectID(satId);
151         template.setObjectName(satId);
152         template.setCenter(new BodyFacade("EARTH", CelestialBodyFactory.getCelestialBodies().getEarth()));
153         template.setReferenceFrame(FrameFacade.map(FramesFactory.getEME2000()));
154         EphemerisOemWriter writer = new EphemerisOemWriter(new WriterBuilder().buildOemWriter(),
155                                                            null, template, fileFormat, "dummy",
156                                                            Constants.JULIAN_DAY, 60);
157         writer.write(tempOem, ephemerisFile);
158 
159         OemParser parser = new ParserBuilder().withMu(body.getGM()).withDefaultInterpolationDegree(2).buildOemParser();
160         EphemerisFile<TimeStampedPVCoordinates, OemSegment> ephemerisFrom = parser.parse(new DataSource(tempOem));
161         Files.delete(Paths.get(tempOem));
162 
163         EphemerisSegment<TimeStampedPVCoordinates> segment = ephemerisFrom.getSatellites().get(satId).getSegments().get(0);
164         Assertions.assertEquals(states.get(0).getDate(), segment.getStart());
165         Assertions.assertEquals(states.get(states.size() - 1).getDate(), segment.getStop());
166         Assertions.assertEquals(states.size(), segment.getCoordinates().size());
167         Assertions.assertEquals(frame, segment.getFrame());
168         Assertions.assertEquals(body.getGM(), segment.getMu(), muTolerance);
169         Assertions.assertEquals(CartesianDerivativesFilter.USE_PV, segment.getAvailableDerivatives());
170         Assertions.assertEquals("GCRF", segment.getFrame().getName());
171         for (int i = 0; i < states.size(); i++) {
172             TimeStampedPVCoordinates expected = states.get(i).getPVCoordinates();
173             TimeStampedPVCoordinates actual = segment.getCoordinates().get(i);
174             Assertions.assertEquals(expected.getDate(), actual.getDate());
175             Assertions.assertEquals(0.0, Vector3D.distance(expected.getPosition(), actual.getPosition()), positionTolerance);
176             Assertions.assertEquals(0.0, Vector3D.distance(expected.getVelocity(), actual.getVelocity()), velocityTolerance);
177         }
178 
179         // test ingested ephemeris generates access intervals
180         final OneAxisEllipsoid parentShape = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
181                 Constants.WGS84_EARTH_FLATTENING, FramesFactory.getITRF(IERSConventions.IERS_2010, true));
182         final double latitude = 0.0;
183         final double longitude = 0.0;
184         final double altitude = 0.0;
185         final GeodeticPoint point = new GeodeticPoint(latitude, longitude, altitude);
186         final TopocentricFrame topo = new TopocentricFrame(parentShape, point, "testPoint1");
187         final ElevationDetector elevationDetector = new ElevationDetector(topo);
188         final EphemerisSegmentPropagator<TimeStampedPVCoordinates> ephemerisSegmentPropagator =
189                         new EphemerisSegmentPropagator<>(segment, new FrameAlignedProvider(segment.getInertialFrame()));
190         final EventsLogger lookupLogger = new EventsLogger();
191         ephemerisSegmentPropagator.addEventDetector(lookupLogger.monitorDetector(elevationDetector));
192 
193         final EventsLogger referenceLogger = new EventsLogger();
194         propagator.clearEventsDetectors();
195         propagator.addEventDetector(referenceLogger.monitorDetector(elevationDetector));
196 
197         propagator.propagate(segment.getStart(), segment.getStop());
198         ephemerisSegmentPropagator.propagate(segment.getStart(), segment.getStop());
199 
200         final double dateEpsilon = 4.2e-5;
201         Assertions.assertTrue(referenceLogger.getLoggedEvents().size() > 0);
202         Assertions.assertEquals(referenceLogger.getLoggedEvents().size(), lookupLogger.getLoggedEvents().size());
203         for (int i = 0; i < referenceLogger.getLoggedEvents().size(); i++) {
204             LoggedEvent reference = referenceLogger.getLoggedEvents().get(i);
205             LoggedEvent actual = lookupLogger.getLoggedEvents().get(i);
206             Assertions.assertEquals(0.0,
207                          FastMath.abs(reference.getState().getDate().durationFrom(actual.getState().getDate())),
208                          dateEpsilon);
209         }
210 
211         final Propagator embeddedPropagator = segment.getPropagator(new FrameAlignedProvider(segment.getInertialFrame()));
212         final EventsLogger embeddedPropLogger = new EventsLogger();
213         embeddedPropagator.addEventDetector(embeddedPropLogger.monitorDetector(elevationDetector));
214         embeddedPropagator.propagate(segment.getStart(), segment.getStop());
215         Assertions.assertEquals(referenceLogger.getLoggedEvents().size(), embeddedPropLogger.getLoggedEvents().size());
216         for (int i = 0; i < referenceLogger.getLoggedEvents().size(); i++) {
217             LoggedEvent reference = referenceLogger.getLoggedEvents().get(i);
218             LoggedEvent actual = embeddedPropLogger.getLoggedEvents().get(i);
219             Assertions.assertEquals(0.0,
220                          FastMath.abs(reference.getState().getDate().durationFrom(actual.getState().getDate())),
221                          dateEpsilon);
222 
223         }
224 
225         final List<SpacecraftState> readInStates = new ArrayList<SpacecraftState>();
226         segment.getCoordinates().forEach(c -> {
227             try {
228                 readInStates.add(new SpacecraftState(new CartesianOrbit(c, frame, mu)));
229             } catch (IllegalArgumentException | OrekitException e) {
230                 Assertions.fail(e.getLocalizedMessage());
231             }
232         });
233 
234         // Create interpolator
235         final int interpolationPoints = 5;
236         final TimeInterpolator<SpacecraftState> interpolator =
237                 new SpacecraftStateInterpolator(interpolationPoints, frame, frame);
238 
239         Ephemeris directEphemProp = new Ephemeris(readInStates, interpolator);
240         final EventsLogger directEphemPropLogger = new EventsLogger();
241         directEphemProp.addEventDetector(directEphemPropLogger.monitorDetector(elevationDetector));
242         directEphemProp.propagate(segment.getStart(), segment.getStop());
243         Assertions.assertEquals(referenceLogger.getLoggedEvents().size(), directEphemPropLogger.getLoggedEvents().size());
244         for (int i = 0; i < referenceLogger.getLoggedEvents().size(); i++) {
245             LoggedEvent reference = referenceLogger.getLoggedEvents().get(i);
246             LoggedEvent actual = directEphemPropLogger.getLoggedEvents().get(i);
247             Assertions.assertEquals(0.0,
248                          FastMath.abs(reference.getState().getDate().durationFrom(actual.getState().getDate())),
249                          dateEpsilon);
250         }
251 
252     }
253 
254 }