1   package org.orekit.files.iirv;
2   
3   import org.junit.jupiter.api.BeforeEach;
4   import org.junit.jupiter.api.Test;
5   import org.orekit.Utils;
6   import org.orekit.annotation.DefaultDataContext;
7   import org.orekit.data.DataSource;
8   import org.orekit.errors.OrekitException;
9   import org.orekit.errors.OrekitIllegalStateException;
10  import org.orekit.files.iirv.terms.CoordinateSystemTerm;
11  import org.orekit.files.iirv.terms.CrossSectionalAreaTerm;
12  import org.orekit.files.iirv.terms.DataSourceTerm;
13  import org.orekit.files.iirv.terms.DragCoefficientTerm;
14  import org.orekit.files.iirv.terms.MassTerm;
15  import org.orekit.files.iirv.terms.MessageClassTerm;
16  import org.orekit.files.iirv.terms.OriginIdentificationTerm;
17  import org.orekit.files.iirv.terms.OriginatorRoutingIndicatorTerm;
18  import org.orekit.files.iirv.terms.RoutingIndicatorTerm;
19  import org.orekit.files.iirv.terms.SolarReflectivityCoefficientTerm;
20  import org.orekit.files.iirv.terms.SupportIdCodeTerm;
21  import org.orekit.files.iirv.terms.VectorTypeTerm;
22  import org.orekit.files.iirv.terms.VehicleIdCodeTerm;
23  import org.orekit.time.AbsoluteDate;
24  import org.orekit.time.DateComponents;
25  import org.orekit.time.TimeScalesFactory;
26  import org.orekit.time.UTCScale;
27  import org.orekit.utils.TimeStampedPVCoordinates;
28  
29  import java.util.Arrays;
30  
31  import static org.junit.jupiter.api.Assertions.assertEquals;
32  import static org.junit.jupiter.api.Assertions.assertFalse;
33  import static org.junit.jupiter.api.Assertions.assertNotEquals;
34  import static org.junit.jupiter.api.Assertions.assertThrows;
35  import static org.junit.jupiter.api.Assertions.assertTrue;
36  
37  public class IIRVMessageTest {
38  
39      private static UTCScale UTC;
40      private IIRVMessage stereoAheadIIRVMessage;
41      private DataSource allVectorsIIRVDataSource;
42      private DataSource firstVectorOnlyIIRVDataSource;
43      private IIRVParser parser;
44  
45      @BeforeEach
46      public void setUp() {
47          // Sets the root of data to read
48          Utils.setDataRoot("regular-data");
49          UTC = TimeScalesFactory.getUTC();
50          parser = new IIRVParser(2024, UTC);
51  
52          final String stereoAheadIirvFile = "/iirv/ahead_20240909_01.iirv";
53          stereoAheadIIRVMessage = parser.parse(
54                  new DataSource(stereoAheadIirvFile, () -> getClass().getResourceAsStream(stereoAheadIirvFile)))
55              .getIIRV();
56  
57          final String testIIRVAllVectors = "/iirv/Test-IIRV-Metadata-All-Vectors.iirv";
58          final String testIIRVFirstVectorOnly = "/iirv/Test-IIRV-Metadata-First-Vector-Only.iirv";
59  
60          allVectorsIIRVDataSource = new DataSource(testIIRVAllVectors, () -> getClass().getResourceAsStream(testIIRVAllVectors));
61          firstVectorOnlyIIRVDataSource = new DataSource(testIIRVFirstVectorOnly, () -> getClass().getResourceAsStream(testIIRVFirstVectorOnly));
62  
63      }
64  
65      @Test
66      void testIIRVMessageConstructors() {
67          IIRVVector first = stereoAheadIIRVMessage.get(0);
68          IIRVVector second = stereoAheadIIRVMessage.get(1);
69  
70          IIRVMessage message1 = new IIRVMessage(first, second);
71          IIRVMessage message2 = new IIRVMessage(Arrays.asList(first, second));
72          IIRVMessage message3 = new IIRVMessage();
73          message3.add(first);
74          message3.add(second);
75          IIRVMessage message4 = parser.parse(allVectorsIIRVDataSource).getIIRV();
76          IIRVMessage message5 = parser.parse(firstVectorOnlyIIRVDataSource).getIIRV();
77          IIRVMessage message6 = new IIRVMessage(message1);
78  
79          // Compare different ways of creating IIRV string
80          assertEquals(message1, message2);
81          assertEquals(message1.toMessageString(IIRVMessage.IncludeMessageMetadata.ALL_VECTORS),
82              message2.toMessageString(IIRVMessage.IncludeMessageMetadata.ALL_VECTORS));
83          assertEquals(message1.toMessageString(IIRVMessage.IncludeMessageMetadata.FIRST_VECTOR_ONLY),
84              message2.toMessageString(IIRVMessage.IncludeMessageMetadata.FIRST_VECTOR_ONLY));
85  
86          // Check against all other created messages
87          assertEquals(message1, message1);
88          assertEquals(message1, message3);
89          assertEquals(message1, message4);
90          assertEquals(message1, message5);
91          assertEquals(message4, message5);
92          assertEquals(message4, message6);
93          assertNotEquals(new IIRVMessage(), null);
94          assertNotEquals(new IIRVMessage(first), first);
95  
96          // Check hashcodes
97          assertEquals(message1.hashCode(), message2.hashCode());
98          assertEquals(message1.hashCode(), message3.hashCode());
99          assertEquals(message1.hashCode(), message4.hashCode());
100         assertEquals(message1.hashCode(), message5.hashCode());
101 
102         // non-increasing exception
103         assertThrows(OrekitIllegalStateException.class, () -> new IIRVMessage(second, first));
104 
105         // Three vectors (ok)
106         IIRVMessage threeVectorMethod = new IIRVMessage(first, second, stereoAheadIIRVMessage.get(2));
107         assertEquals(3, threeVectorMethod.size());
108     }
109 
110     @Test
111     void testConstructorErrorHandling() {
112         // Blank file
113         final String blankFile = "/iirv/invalid/blank.iirv";
114         assertThrows(OrekitException.class, () -> parser.parse(
115             new DataSource(blankFile, () -> getClass().getResourceAsStream(blankFile))
116         ));
117 
118         // Wrong sequence numbers
119         assertThrows(OrekitIllegalStateException.class, () -> new IIRVMessage(stereoAheadIIRVMessage.get(0),  // (1,3)
120             stereoAheadIIRVMessage.get(2)));
121         assertThrows(OrekitIllegalStateException.class, () -> new IIRVMessage(stereoAheadIIRVMessage.get(0), // (1,2,4)
122             stereoAheadIIRVMessage.get(1), stereoAheadIIRVMessage.get(3)));
123 
124         // Missing line 5
125         final String missingLine5 = "/iirv/invalid/missing_line5.iirv";
126         assertThrows(OrekitException.class, () -> parser.parse(
127             new DataSource(missingLine5, () -> getClass().getResourceAsStream(missingLine5))
128         ));
129 
130         // Missing a line break
131         final String missingLinebeak = "/iirv/invalid/missing_linebreak.iirv";
132         assertThrows(OrekitException.class, () -> parser.parse(
133             new DataSource(missingLinebeak, () -> getClass().getResourceAsStream(missingLinebeak))
134         ));
135     }
136 
137     @Test
138     void testMethods() {
139 
140         // Test "isEmpty" method
141         assertTrue(new IIRVMessage().isEmpty());
142         assertEquals(0, new IIRVMessage().size());
143         assertFalse(stereoAheadIIRVMessage.isEmpty());
144         assertEquals(97, stereoAheadIIRVMessage.size());
145 
146         // Test "getVectors"
147         assertEquals(stereoAheadIIRVMessage.get(0), stereoAheadIIRVMessage.getVectors().get(0));
148     }
149 
150     @Test
151     void testIIRVMessageValidation() {
152         IIRVVector first = stereoAheadIIRVMessage.get(0);
153         IIRVVector second = stereoAheadIIRVMessage.get(1);
154 
155         TimeStampedPVCoordinates c = second.getTimeStampedPVCoordinates(2024);
156 
157         IIRVBuilder iirvBuilder = new IIRVBuilder(UTC);
158         iirvBuilder.setMessageID(second.getMessageID());
159         iirvBuilder.setMessageClass(second.getMessageClass());
160         iirvBuilder.setOriginIdentification(second.getOriginIdentification());
161         iirvBuilder.setRoutingIndicator(second.getRoutingIndicator());
162         iirvBuilder.setVectorType(second.getVectorType());
163         iirvBuilder.setDataSource(second.getDataSource());
164         iirvBuilder.setSupportIdCode(second.getSupportIdCode());
165         iirvBuilder.setVehicleIdCode(second.getVehicleIdCode());
166         iirvBuilder.setSequenceNumber(second.getSequenceNumber());
167         iirvBuilder.setMass(second.getMass());
168         iirvBuilder.setCrossSectionalArea(second.getCrossSectionalArea());
169         iirvBuilder.setDragCoefficient(second.getDragCoefficient());
170         iirvBuilder.setSolarReflectivityCoefficient(second.getSolarReflectivityCoefficient());
171         iirvBuilder.setOriginatorRoutingIndicator(second.getOriginatorRoutingIndicator());
172 
173         // Message ID term
174         iirvBuilder.setMessageID(0);
175         assertThrows(OrekitIllegalStateException.class, () -> new IIRVMessage(first, iirvBuilder.buildVector(c)));
176         iirvBuilder.setMessageID(second.getMessageID());
177 
178         // Message Class term
179         iirvBuilder.setMessageClass(MessageClassTerm.INFLIGHT_UPDATE);
180         assertThrows(OrekitIllegalStateException.class, () -> new IIRVMessage(first, iirvBuilder.buildVector(c)));
181         iirvBuilder.setMessageClass(second.getMessageClass());
182 
183         // Origin ID term
184         iirvBuilder.setOriginIdentification(OriginIdentificationTerm.JPL);
185         assertThrows(OrekitIllegalStateException.class, () -> new IIRVMessage(first, iirvBuilder.buildVector(c)));
186         iirvBuilder.setOriginIdentification(second.getOriginIdentification());
187 
188         // Routing indicator term
189         iirvBuilder.setRoutingIndicator(RoutingIndicatorTerm.ETR);
190         assertThrows(OrekitIllegalStateException.class, () -> new IIRVMessage(first, iirvBuilder.buildVector(c)));
191         iirvBuilder.setRoutingIndicator(second.getRoutingIndicator());
192 
193         // Vector type term
194         iirvBuilder.setVectorType(VectorTypeTerm.REENTRY);
195         assertThrows(OrekitIllegalStateException.class, () -> new IIRVMessage(first, iirvBuilder.buildVector(c)));
196         iirvBuilder.setVectorType(second.getVectorType());
197 
198         // Data source
199         iirvBuilder.setDataSource(DataSourceTerm.OFFLINE);
200         assertThrows(OrekitIllegalStateException.class, () -> new IIRVMessage(first, iirvBuilder.buildVector(c)));
201         iirvBuilder.setDataSource(second.getDataSource());
202 
203         // Coordinate system term
204         iirvBuilder.setCoordinateSystem(CoordinateSystemTerm.HELIOCENTRIC_B1950);
205         assertThrows(OrekitIllegalStateException.class, () -> new IIRVMessage(first, iirvBuilder.buildVector(c)));
206         iirvBuilder.setCoordinateSystem(second.getCoordinateSystem());
207 
208         // SIC term
209         iirvBuilder.setSupportIdCode(new SupportIdCodeTerm(1));
210         assertThrows(OrekitIllegalStateException.class, () -> new IIRVMessage(first, iirvBuilder.buildVector(c)));
211         iirvBuilder.setSupportIdCode(second.getSupportIdCode());
212 
213         // VID term
214         iirvBuilder.setVehicleIdCode(new VehicleIdCodeTerm(2));
215         assertThrows(OrekitIllegalStateException.class, () -> new IIRVMessage(first, iirvBuilder.buildVector(c)));
216         iirvBuilder.setVehicleIdCode(second.getVehicleIdCode());
217 
218     }
219 
220     @Test
221     void testSequenceNumberMonotonic() {
222         IIRVVector vec1 = stereoAheadIIRVMessage.get(0);
223         IIRVVector origVec2 = stereoAheadIIRVMessage.get(1);
224 
225         String line2SkipSequenceNumber = "1111023401003253001500000033";
226         IIRVVector vec2 = new IIRVVector(origVec2.buildLine1(true), line2SkipSequenceNumber,
227             origVec2.buildLine3(), origVec2.buildLine4(), origVec2.buildLine5(), origVec2.buildLine6(), UTC);
228 
229         assertThrows(OrekitIllegalStateException.class, () -> new IIRVMessage(vec1, vec2));
230     }
231 
232     /**
233      * Tests IIRV files from the STEREO mission.
234      * <p>
235      * Source: <a href="https://stereo-ssc.nascom.nasa.gov/stations/iirv/">Stereo Science Center</a>
236      */
237     @Test
238     @DefaultDataContext
239     void testStereoIIRV() {
240         final UTCScale utc = TimeScalesFactory.getUTC();
241 
242         // Tests "ahead" IIRV file from the STEREO mission.
243         //  Source: https://stereo-ssc.nascom.nasa.gov/stations/iirv/
244 
245         // Website delivers one IIRV vector for each day at 00:00 UTC
246         AbsoluteDate aheadAbsoluteDate = new AbsoluteDate(
247             2024, 9, 9, 0, 0, 0, utc);
248         DateComponents aheadDate = aheadAbsoluteDate.getComponents(utc).getDate();
249 
250         // Test ahead IIRV Vector
251         IIRVVector iirv = stereoAheadIIRVMessage.get(0);
252         assertEquals(iirv, iirv); // Trivial self-equality check
253 
254         // Check Line 1
255         assertEquals(1234567, iirv.getMessageID().value());
256         assertEquals(MessageClassTerm.NOMINAL, iirv.getMessageClass());
257         assertEquals(OriginIdentificationTerm.GSFC, iirv.getOriginIdentification());
258         assertEquals(RoutingIndicatorTerm.MANY, iirv.getRoutingIndicator());
259 
260         // Check Line 2
261         assertEquals(VectorTypeTerm.FREE_FLIGHT, iirv.getVectorType());
262         assertEquals(DataSourceTerm.NOMINAL, iirv.getDataSource());
263         assertEquals(CoordinateSystemTerm.GEOCENTRIC_TRUE_OF_DATE_ROTATING, iirv.getCoordinateSystem());
264         assertEquals(234, iirv.getSupportIdCode().value());
265         assertEquals(1, iirv.getVehicleIdCode().value());
266         assertEquals(1, iirv.getSequenceNumber().value());
267         assertEquals(aheadDate, iirv.getDayOfYear().getDateComponents(aheadDate.getYear()));
268 
269         // Check Line 3
270         assertEquals(-17325900294L, iirv.getXPosition().value());
271         assertEquals(55126516659L, iirv.getYPosition().value());
272         assertEquals(25045637815L, iirv.getZPosition().value());
273 
274         // Check Line 4
275         assertEquals(4007847.475, iirv.getXVelocity().value());
276         assertEquals(1261889.943, iirv.getYVelocity().value());
277         assertEquals(325.189, iirv.getZVelocity().value());
278 
279         // Check Line 5
280         assertEquals(MassTerm.UNUSED, iirv.getMass());
281         assertEquals(CrossSectionalAreaTerm.UNUSED, iirv.getCrossSectionalArea());
282         assertEquals(DragCoefficientTerm.UNUSED, iirv.getDragCoefficient());
283         assertEquals(SolarReflectivityCoefficientTerm.UNUSED, iirv.getSolarReflectivityCoefficient());
284 
285         // Check Line 6
286         assertEquals(OriginatorRoutingIndicatorTerm.GAQD, iirv.getOriginatorRoutingIndicator());
287     }
288 
289     @Test
290     void parseSingleVectorFile() {
291         final String iirvFile = "/iirv/ISS_ZARYA_25544_NASA_IIRV.iirv";  // IIRV for the ISS, generated by STK
292         final DataSource source = new DataSource(iirvFile, () -> getClass().getResourceAsStream(iirvFile));
293 
294         IIRVMessage iirvMessage = parser.parse(source).getIIRV();
295         assertEquals(1, iirvMessage.size()); // Verify only one vector is loaded
296 
297         // Validate a few arbitrary fields
298         assertEquals(2.2, iirvMessage.get(0).getDragCoefficient().value());
299         assertEquals(0, iirvMessage.get(0).getMessageID().value());
300     }
301 
302     @Test
303     void parseMultipleVectorFile() {
304         // Path to a TLE generated for the international space station
305         final String iirvFile = "/iirv/ISS_ZARYA_25544_NASA_IIRV_1DAY.iirv";
306         final DataSource source = new DataSource(iirvFile, () -> getClass().getResourceAsStream(iirvFile));
307 
308         IIRVMessage iirvMessage = parser.parse(source).getIIRV();
309         assertEquals(6, iirvMessage.size());
310 
311         // Validate the sequence numbers are in order from 0-5
312         for (int i = 0; i < 6; i++) {
313             assertEquals(i, iirvMessage.get(i).getSequenceNumber().value());
314         }
315     }
316 
317 
318 }