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.general;
18  
19  import static org.junit.Assert.assertEquals;
20  import static org.junit.Assert.assertNotNull;
21  
22  import java.io.BufferedWriter;
23  import java.io.IOException;
24  import java.nio.charset.StandardCharsets;
25  import java.nio.file.Files;
26  import java.nio.file.Paths;
27  import java.util.ArrayList;
28  import java.util.List;
29  
30  import org.hipparchus.geometry.euclidean.threed.Rotation;
31  import org.junit.Assert;
32  import org.junit.Before;
33  import org.junit.Test;
34  import org.orekit.Utils;
35  import org.orekit.attitudes.Attitude;
36  import org.orekit.attitudes.AttitudeProvider;
37  import org.orekit.attitudes.InertialProvider;
38  import org.orekit.bodies.CelestialBody;
39  import org.orekit.bodies.CelestialBodyFactory;
40  import org.orekit.data.DataSource;
41  import org.orekit.errors.OrekitIllegalArgumentException;
42  import org.orekit.errors.OrekitMessages;
43  import org.orekit.files.ccsds.definitions.CelestialBodyFrame;
44  import org.orekit.files.ccsds.definitions.FrameFacade;
45  import org.orekit.files.ccsds.definitions.SpacecraftBodyFrame;
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.ndm.adm.AttitudeType;
50  import org.orekit.files.ccsds.ndm.adm.aem.AemMetadata;
51  import org.orekit.files.ccsds.ndm.adm.aem.AemSegment;
52  import org.orekit.files.ccsds.ndm.adm.aem.AttitudeWriter;
53  import org.orekit.files.ccsds.utils.FileFormat;
54  import org.orekit.files.general.AttitudeEphemerisFile.AttitudeEphemerisSegment;
55  import org.orekit.files.general.OrekitAttitudeEphemerisFile.OrekitSatelliteAttitudeEphemeris;
56  import org.orekit.frames.Frame;
57  import org.orekit.frames.FramesFactory;
58  import org.orekit.orbits.KeplerianOrbit;
59  import org.orekit.orbits.PositionAngle;
60  import org.orekit.propagation.SpacecraftState;
61  import org.orekit.propagation.analytical.KeplerianPropagator;
62  import org.orekit.time.AbsoluteDate;
63  import org.orekit.utils.AngularDerivativesFilter;
64  import org.orekit.utils.Constants;
65  import org.orekit.utils.TimeStampedAngularCoordinates;
66  
67  public class OrekitAttitudeEphemerisFileTest {
68  
69      @Before
70      public void setUp() throws Exception {
71          Utils.setDataRoot("regular-data");
72      }
73  
74      @Test
75      public void testGetSatellites() {
76          final String id1 = "ID1";
77          final String id2 = "ID2";
78          OrekitAttitudeEphemerisFile file = new OrekitAttitudeEphemerisFile();
79          OrekitSatelliteAttitudeEphemeris ephem1 = file.addSatellite(id1);
80          assertNotNull(ephem1);
81          OrekitSatelliteAttitudeEphemeris ephem2 = file.addSatellite(id2);
82          assertNotNull(ephem2);
83      }
84  
85      @Test
86      public void testWritingToAEM() throws IOException {
87          final double quaternionTolerance = 1e-5;
88          final String satId = "SATELLITE1";
89          final double sma = 10000000;
90          final double inc = Math.toRadians(45.0);
91          final double ecc = 0.1;
92          final double raan = 0.0;
93          final double pa = 0.0;
94          final double ta = 0.0;
95          final AbsoluteDate date = new AbsoluteDate();
96          final Frame frame = FramesFactory.getEME2000();
97          final CelestialBody body = CelestialBodyFactory.getEarth();
98          final double mu = body.getGM();
99          KeplerianOrbit initialOrbit = new KeplerianOrbit(sma, ecc, inc, pa, raan, ta, PositionAngle.TRUE,
100                                                          frame, date, mu);
101 
102         // Initialize a Keplerian propagator with an Inertial attitude provider
103         // It is expected that all attitude data lines will have the same value
104         final Rotation refRot = new Rotation(0.72501, -0.64585, 0.018542, -0.23854, false);
105         AttitudeProvider inertialPointing = new InertialProvider(refRot);
106         KeplerianPropagator propagator = new KeplerianPropagator(initialOrbit, inertialPointing);
107 
108         final double propagationDurationSeconds = 1200.0;
109         final double stepSizeSeconds = 60.0;
110         List<SpacecraftState> states = new ArrayList<SpacecraftState>();
111 
112         for (double dt = 0.0; dt < propagationDurationSeconds; dt += stepSizeSeconds) {
113             states.add(propagator.propagate(date.shiftedBy(dt)));
114         }
115 
116         OrekitAttitudeEphemerisFile ephemerisFile = new OrekitAttitudeEphemerisFile();
117         OrekitSatelliteAttitudeEphemeris satellite = ephemerisFile.addSatellite(satId);
118         satellite.addNewSegment(states,
119                                 OrekitSatelliteAttitudeEphemeris.DEFAULT_INTERPOLATION_METHOD,
120                                 OrekitSatelliteAttitudeEphemeris.DEFAULT_INTERPOLATION_SIZE,
121                                 AngularDerivativesFilter.USE_RR);
122 
123         // Test of all getters for OrekitSatelliteAttitudeEphemeris
124         assertEquals(satId, satellite.getId());
125         assertEquals(0.0, states.get(0).getDate().durationFrom(satellite.getStart()), 1.0e-15);
126         assertEquals(0.0, states.get(states.size() - 1).getDate().durationFrom(satellite.getStop()), 1.0e-15);
127 
128         // Test of all getters for OrekitAttitudeEphemerisSegment
129         AttitudeEphemerisSegment<TimeStampedAngularCoordinates> segment = satellite.getSegments().get(0);
130         assertEquals(OrekitSatelliteAttitudeEphemeris.DEFAULT_INTERPOLATION_METHOD, segment.getInterpolationMethod());
131         assertEquals(OrekitSatelliteAttitudeEphemeris.DEFAULT_INTERPOLATION_SIZE, segment.getInterpolationSamples());
132         assertEquals(0.0, states.get(0).getDate().durationFrom(segment.getStart()), 1.0e-15);
133         assertEquals(0.0, states.get(states.size() - 1).getDate().durationFrom(segment.getStop()), 1.0e-15);
134         Assert.assertEquals(AngularDerivativesFilter.USE_RR, segment.getAvailableDerivatives());
135 
136         // Verify attitude
137         final Attitude attitude = segment.getAttitudeProvider().getAttitude(initialOrbit, date, frame);
138         Assert.assertEquals(frame, attitude.getReferenceFrame());
139         Assert.assertEquals(refRot.getQ0(), attitude.getRotation().getQ0(), quaternionTolerance);
140         Assert.assertEquals(refRot.getQ1(), attitude.getRotation().getQ1(), quaternionTolerance);
141         Assert.assertEquals(refRot.getQ2(), attitude.getRotation().getQ2(), quaternionTolerance);
142         Assert.assertEquals(refRot.getQ3(), attitude.getRotation().getQ3(), quaternionTolerance);
143 
144         String tempAem = Files.createTempFile("OrekitAttitudeEphemerisFileTest", ".aem").toString();
145         try (BufferedWriter writer = Files.newBufferedWriter(Paths.get(tempAem), StandardCharsets.UTF_8)) {
146             new AttitudeWriter(new WriterBuilder().buildAemWriter(),
147                                null, dummyMetadata(), FileFormat.KVN, "", 60).write(writer, ephemerisFile);
148         }
149 
150         AttitudeEphemerisFile<TimeStampedAngularCoordinates, AemSegment> ephemerisFrom =
151                         new ParserBuilder().buildAemParser().parseMessage(new DataSource(tempAem));
152         Files.delete(Paths.get(tempAem));
153         
154         segment = ephemerisFrom.getSatellites().get(satId).getSegments().get(0);
155         assertEquals(states.get(0).getDate(), segment.getStart());
156         assertEquals(states.get(states.size() - 1).getDate(), segment.getStop());
157         assertEquals(states.size(), segment.getAngularCoordinates().size());
158         for (int i = 0; i < states.size(); i++) {
159             TimeStampedAngularCoordinates expected = states.get(i).getAttitude().getOrientation();
160             TimeStampedAngularCoordinates actual = segment.getAngularCoordinates().get(i);
161             assertEquals(expected.getDate(), actual.getDate());
162             assertEquals(0.0, Rotation.distance(refRot, actual.getRotation()), quaternionTolerance);
163         }
164 
165     }
166 
167     @Test
168     public void testNoStates() {
169 
170         // Satellite ID
171         final String satId = "SATELLITE1";
172 
173         // Create an empty list of states
174         List<SpacecraftState> states = new ArrayList<SpacecraftState>();
175 
176         // Create a new satellite attitude ephemeris
177         OrekitAttitudeEphemerisFile ephemerisFile = new OrekitAttitudeEphemerisFile();
178         OrekitSatelliteAttitudeEphemeris satellite = ephemerisFile.addSatellite(satId);
179 
180         // Try to add a new segment
181         try {
182             satellite.addNewSegment(states,
183                                     OrekitSatelliteAttitudeEphemeris.DEFAULT_INTERPOLATION_METHOD,
184                                     OrekitSatelliteAttitudeEphemeris.DEFAULT_INTERPOLATION_SIZE,
185                                     AngularDerivativesFilter.USE_RR);
186         } catch (OrekitIllegalArgumentException oiae) {
187             Assert.assertEquals(OrekitMessages.NULL_ARGUMENT, oiae.getSpecifier());
188         }
189 
190 
191     }
192 
193     @Test
194     public void testNoEnoughDataForInterpolation() {
195 
196         // Create a spacecraft state
197         final String satId = "SATELLITE1";
198         final double sma = 10000000;
199         final double inc = Math.toRadians(45.0);
200         final double ecc = 0.1;
201         final double raan = 0.0;
202         final double pa = 0.0;
203         final double ta = 0.0;
204         final AbsoluteDate date = new AbsoluteDate();
205         final Frame frame = FramesFactory.getEME2000();
206         final CelestialBody body = CelestialBodyFactory.getEarth();
207         final double mu = body.getGM();
208         KeplerianOrbit initialOrbit = new KeplerianOrbit(sma, ecc, inc, pa, raan, ta, PositionAngle.TRUE,
209                                                          frame, date, mu);
210         SpacecraftState state = new SpacecraftState(initialOrbit);
211 
212         // Add the state to the list of spacecraft states
213         List<SpacecraftState> states = new ArrayList<SpacecraftState>();
214         states.add(state);
215 
216         // Create a new satellite attitude ephemeris
217         OrekitAttitudeEphemerisFile ephemerisFile = new OrekitAttitudeEphemerisFile();
218         OrekitSatelliteAttitudeEphemeris satellite = ephemerisFile.addSatellite(satId);
219 
220         // Try to add a new segment
221         try {
222             satellite.addNewSegment(states, "LINEAR", 1, AngularDerivativesFilter.USE_R);
223         } catch (OrekitIllegalArgumentException oiae) {
224             Assert.assertEquals(OrekitMessages.NOT_ENOUGH_DATA_FOR_INTERPOLATION, oiae.getSpecifier());
225         }
226     }
227 
228     private AemMetadata dummyMetadata() {
229         AemMetadata metadata = new AemMetadata(4);
230         metadata.setTimeSystem(TimeSystem.TT);
231         metadata.setObjectID("SATELLITE1");
232         metadata.setObjectName("transgalactic");
233         metadata.getEndpoints().setFrameA(new FrameFacade(FramesFactory.getGCRF(), CelestialBodyFrame.GCRF,
234                                                           null, null, "GCRF"));
235         metadata.getEndpoints().setFrameB(new FrameFacade(null, null, null,
236                                                           new SpacecraftBodyFrame(SpacecraftBodyFrame.BaseEquipment.GYRO_FRAME, "1"),
237                                                           "GYRO FRAME 1"));
238         metadata.getEndpoints().setA2b(true);
239         metadata.setStartTime(AbsoluteDate.J2000_EPOCH.shiftedBy(80 * Constants.JULIAN_CENTURY));
240         metadata.setStopTime(metadata.getStartTime().shiftedBy(Constants.JULIAN_YEAR));
241         metadata.setAttitudeType(AttitudeType.QUATERNION_DERIVATIVE);
242         metadata.setIsFirst(true);
243         return metadata;
244     }
245 
246 }
247