1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.files.rinex.observation;
18
19 import java.io.ByteArrayInputStream;
20 import java.io.CharArrayWriter;
21 import java.io.IOException;
22 import java.nio.charset.StandardCharsets;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.function.BiFunction;
26 import java.util.function.Function;
27
28 import org.hipparchus.geometry.euclidean.threed.Vector3D;
29 import org.hipparchus.util.FastMath;
30 import org.hipparchus.util.Precision;
31 import org.junit.jupiter.api.Assertions;
32 import org.junit.jupiter.api.BeforeEach;
33 import org.junit.jupiter.api.Test;
34 import org.orekit.Utils;
35 import org.orekit.annotation.DefaultDataContext;
36 import org.orekit.data.DataContext;
37 import org.orekit.data.DataSource;
38 import org.orekit.errors.OrekitException;
39 import org.orekit.errors.OrekitMessages;
40 import org.orekit.estimation.measurements.QuadraticClockModel;
41 import org.orekit.files.rinex.AppliedDCBS;
42 import org.orekit.files.rinex.AppliedPCVS;
43 import org.orekit.files.rinex.section.RinexComment;
44 import org.orekit.gnss.ObservationTimeScale;
45 import org.orekit.gnss.ObservationType;
46 import org.orekit.gnss.PredefinedObservationType;
47 import org.orekit.gnss.SatInSystem;
48 import org.orekit.gnss.SatelliteSystem;
49 import org.orekit.time.AbsoluteDate;
50 import org.orekit.time.ConstantOffsetTimeScale;
51 import org.orekit.time.TimeOffset;
52 import org.orekit.time.TimeScale;
53 import org.orekit.time.TimeScales;
54
55 public class RinexObservationWriterTest {
56
57 @BeforeEach
58 public void setUp() {
59 Utils.setDataRoot("regular-data");
60 }
61
62 @DefaultDataContext
63 @Test
64 public void testWriteHeaderTwice() throws IOException {
65 final RinexObservation robs = load("rinex/bbbb0000.00o",
66 PredefinedObservationType::valueOf,
67 (system, timeScales) -> system.getObservationTimeScale().getTimeScale(timeScales),
68 DataContext.getDefault().getTimeScales());
69 final CharArrayWriter caw = new CharArrayWriter();
70 try (RinexObservationWriter writer = new RinexObservationWriter(caw, "dummy")) {
71 writer.setReceiverClockModel(robs.extractClockModel(2));
72 writer.prepareComments(robs.getComments());
73 writer.writeHeader(robs.getHeader());
74 writer.writeHeader(robs.getHeader());
75 Assertions.fail("an exception should have been thrown");
76 } catch (OrekitException oe) {
77 Assertions.assertEquals(OrekitMessages.HEADER_ALREADY_WRITTEN, oe.getSpecifier());
78 Assertions.assertEquals("dummy", oe.getParts()[0]);
79 }
80 }
81
82 @DefaultDataContext
83 @Test
84 public void testTooLongAgency() throws IOException {
85 final RinexObservation robs = load("rinex/bbbb0000.00o",
86 PredefinedObservationType::valueOf,
87 (system, timeScales) -> system.getObservationTimeScale().getTimeScale(timeScales),
88 DataContext.getDefault().getTimeScales());
89 robs.getHeader().setAgencyName("much too long agency name exceeding 40 characters");
90 final CharArrayWriter caw = new CharArrayWriter();
91 try (RinexObservationWriter writer = new RinexObservationWriter(caw, "dummy")) {
92 writer.setReceiverClockModel(robs.extractClockModel(2));
93 writer.prepareComments(robs.getComments());
94 writer.writeHeader(robs.getHeader());
95 Assertions.fail("an exception should have been thrown");
96 } catch (OrekitException oe) {
97 Assertions.assertEquals(OrekitMessages.FIELD_TOO_LONG, oe.getSpecifier());
98 Assertions.assertEquals("much too long agency name exceeding 40 characters", oe.getParts()[0]);
99 Assertions.assertEquals(40, (Integer) oe.getParts()[1]);
100 }
101 }
102
103 @DefaultDataContext
104 @Test
105 public void testNoWriteHeader() throws IOException {
106 final RinexObservation robs = load("rinex/aiub0000.00o",
107 PredefinedObservationType::valueOf,
108 (system, timeScales) -> system.getObservationTimeScale().getTimeScale(timeScales),
109 DataContext.getDefault().getTimeScales());
110 final CharArrayWriter caw = new CharArrayWriter();
111 try (RinexObservationWriter writer = new RinexObservationWriter(caw, "dummy")) {
112 writer.setReceiverClockModel(robs.extractClockModel(2));
113 writer.writeObservationDataSet(robs.getObservationDataSets().get(0));
114 Assertions.fail("an exception should have been thrown");
115 } catch (OrekitException oe) {
116 Assertions.assertEquals(OrekitMessages.HEADER_NOT_WRITTEN, oe.getSpecifier());
117 Assertions.assertEquals("dummy", oe.getParts()[0]);
118 }
119 }
120
121 @DefaultDataContext
122 @Test
123 public void testRoundTripRinex2A() throws IOException {
124 doTestRoundTrip("rinex/aiub0000.00o", 0.0);
125 }
126
127 @DefaultDataContext
128 @Test
129 public void testRoundTripRinex2B() throws IOException {
130 doTestRoundTrip("rinex/cccc0000.07o", 0.0);
131 }
132
133 @DefaultDataContext
134 @Test
135 public void testRoundTripRinex3A() throws IOException {
136 doTestRoundTrip("rinex/bbbb0000.00o", 0.0);
137 }
138
139 @DefaultDataContext
140 @Test
141 public void testRoundTripRinex3B() throws IOException {
142 doTestRoundTrip("rinex/dddd0000.01o", 0.0);
143 }
144
145 @DefaultDataContext
146 @Test
147 public void testRoundTripDcbs() throws IOException {
148 doTestRoundTrip("rinex/dcbs.00o", 0.0);
149 }
150
151 @DefaultDataContext
152 @Test
153 public void testRoundTripPcvs() throws IOException {
154 doTestRoundTrip("rinex/pcvs.00o", 0.0);
155 }
156
157 @DefaultDataContext
158 @Test
159 public void testRoundTripScaleFactor() throws IOException {
160 doTestRoundTrip("rinex/bbbb0000.00o", 0.0);
161 }
162
163 @DefaultDataContext
164 @Test
165 public void testRoundTripObsScaleFactor() throws IOException {
166 doTestRoundTrip("rinex/ice12720-scaled.07o", 0.0);
167 }
168
169 @DefaultDataContext
170 @Test
171 public void testRoundTripLeapSecond() throws IOException {
172 doTestRoundTrip("rinex/jnu10110.17o", 0.0);
173 }
174
175 @DefaultDataContext
176 @Test
177 public void testContinuationPhaseShift() throws IOException {
178 doTestRoundTrip("rinex/continuation-phase-shift.23o", 0.0);
179 }
180
181 @DefaultDataContext
182 @Test
183 public void testCustomSystem() throws IOException {
184 doTestRoundTrip("rinex/custom-system.01o", 0.0,
185 CustomType::new,
186 (system, timeScales) -> {
187 final ObservationTimeScale ots = system.getObservationTimeScale();
188 return ots != null ?
189 ots.getTimeScale(timeScales) :
190 new ConstantOffsetTimeScale(system.name(), new TimeOffset(45, TimeOffset.SECOND));
191 },
192 DataContext.getDefault().getTimeScales());
193 }
194
195 private RinexObservation load(final String name,
196 final Function<? super String, ? extends ObservationType> typeBuilder,
197 final BiFunction<SatelliteSystem, TimeScales, ? extends TimeScale> timeScaleBuilder,
198 final TimeScales timeScales) {
199 final DataSource dataSource = new DataSource(name, () -> Utils.class.getClassLoader().getResourceAsStream(name));
200 return new RinexObservationParser(typeBuilder, timeScaleBuilder, timeScales).parse(dataSource);
201 }
202
203 @DefaultDataContext
204 private void doTestRoundTrip(final String resourceName, double expectedDt) throws IOException {
205 doTestRoundTrip(resourceName, expectedDt,
206 PredefinedObservationType::valueOf,
207 (system, timeScales) -> system.getObservationTimeScale() == null ?
208 null :
209 system.getObservationTimeScale().getTimeScale(timeScales),
210 DataContext.getDefault().getTimeScales());
211 }
212
213 private void doTestRoundTrip(final String resourceName, double expectedDt,
214 final Function<? super String, ? extends ObservationType> typeBuilder,
215 final BiFunction<SatelliteSystem, TimeScales, ? extends TimeScale> timeScaleBuilder,
216 final TimeScales timeScales) throws IOException {
217
218 final RinexObservation robs = load(resourceName, typeBuilder, timeScaleBuilder, timeScales);
219 final CharArrayWriter caw = new CharArrayWriter();
220 try (RinexObservationWriter writer = new RinexObservationWriter(caw, "dummy", timeScaleBuilder, timeScales)) {
221 writer.setReceiverClockModel(robs.extractClockModel(2));
222 RinexObservation patched = load(resourceName, typeBuilder, timeScaleBuilder, timeScales);
223 patched.getHeader().setClockOffsetApplied(robs.getHeader().getClockOffsetApplied());
224 if (FastMath.abs(expectedDt) > 1.0e-15) {
225 writer.setReceiverClockModel(new QuadraticClockModel(robs.getHeader().getTFirstObs(),
226 expectedDt, 0.0, 0.0));
227 }
228 writer.writeCompleteFile(patched);
229 }
230
231
232 final byte[] bytes = caw.toString().getBytes(StandardCharsets.UTF_8);
233 final DataSource source = new DataSource("", () -> new ByteArrayInputStream(bytes));
234 final RinexObservation rebuilt = new RinexObservationParser(typeBuilder, timeScaleBuilder, timeScales).parse(source);
235
236 checkRinexFile(robs, rebuilt, expectedDt);
237
238 }
239
240 private void checkRinexFile(final RinexObservation first, final RinexObservation second,
241 final double expectedDt) {
242 checkRinexHeader(first.getHeader(), second.getHeader(), expectedDt);
243
244 Assertions.assertTrue(first.getComments().size() >= second.getComments().size());
245 for (int i = 0; i < second.getComments().size(); ++i) {
246 checkRinexComments(first.getComments().get(i), second.getComments().get(i));
247 }
248 Assertions.assertEquals(first.getObservationDataSets().size(), second.getObservationDataSets().size());
249 for (int i = 0; i < first.getObservationDataSets().size(); ++i) {
250 checkRinexObs(first.getObservationDataSets().get(i), second.getObservationDataSets().get(i), expectedDt);
251 }
252 }
253
254 private void checkRinexHeader(final RinexObservationHeader first, final RinexObservationHeader second,
255 final double expectedDt) {
256 Assertions.assertEquals(first.getFormatVersion(), second.getFormatVersion(), 0.001);
257 Assertions.assertEquals(first.getSatelliteSystem(), second.getSatelliteSystem());
258 Assertions.assertEquals(first.getProgramName(), second.getProgramName());
259 Assertions.assertEquals(first.getRunByName(), second.getRunByName());
260 Assertions.assertEquals(first.getCreationDateComponents(), second.getCreationDateComponents());
261 Assertions.assertEquals(first.getCreationTimeZone(), second.getCreationTimeZone());
262 checkDate(first.getCreationDate(), second.getCreationDate(), 0.0);
263 Assertions.assertEquals(first.getDoi(), second.getDoi());
264 Assertions.assertEquals(first.getLicense(), second.getLicense());
265 Assertions.assertEquals(first.getStationInformation(), second.getStationInformation());
266 Assertions.assertEquals(first.getMarkerName(), second.getMarkerName());
267 Assertions.assertEquals(first.getMarkerNumber(), second.getMarkerNumber());
268 Assertions.assertEquals(first.getObserverName(), second.getObserverName());
269 Assertions.assertEquals(first.getAgencyName(), second.getAgencyName());
270 Assertions.assertEquals(first.getReceiverNumber(), second.getReceiverNumber());
271 Assertions.assertEquals(first.getReceiverType(), second.getReceiverType());
272 Assertions.assertEquals(first.getReceiverVersion(), second.getReceiverVersion());
273 Assertions.assertEquals(first.getAntennaNumber(), second.getAntennaNumber());
274 Assertions.assertEquals(first.getAntennaType(), second.getAntennaType());
275 checkVector(first.getApproxPos(), second.getApproxPos());
276 Assertions.assertEquals(first.getAntennaHeight(), second.getAntennaHeight(), 1.0e-12);
277 Assertions.assertEquals(first.getEccentricities().getX(), second.getEccentricities().getX(), 1.0e-12);
278 Assertions.assertEquals(first.getEccentricities().getY(), second.getEccentricities().getY(), 1.0e-12);
279 Assertions.assertEquals(first.getClockOffsetApplied(), second.getClockOffsetApplied());
280 Assertions.assertEquals(first.getInterval(), second.getInterval(), 1.0e-12);
281 checkDate(first.getTFirstObs(), second.getTFirstObs(), expectedDt);
282 checkDate(first.getTLastObs(), second.getTLastObs(), expectedDt);
283 Assertions.assertEquals(first.getLeapSeconds(), second.getLeapSeconds());
284 Assertions.assertEquals(first.getMarkerType(), second.getMarkerType());
285 checkVector(first.getAntennaReferencePoint(), second.getAntennaReferencePoint());
286 Assertions.assertEquals(first.getObservationCode(), second.getObservationCode());
287 checkVector(first.getAntennaPhaseCenter(), second.getAntennaPhaseCenter());
288 checkVector(first.getAntennaBSight(), second.getAntennaBSight());
289 Assertions.assertEquals(first.getAntennaAzimuth(), second.getAntennaAzimuth(), 1.0e-12);
290 checkVector(first.getAntennaZeroDirection(), second.getAntennaZeroDirection());
291 checkVector(first.getCenterMass(), second.getCenterMass());
292 Assertions.assertEquals(first.getSignalStrengthUnit(), second.getSignalStrengthUnit());
293 Assertions.assertEquals(first.getLeapSecondsFuture(), second.getLeapSecondsFuture());
294 Assertions.assertEquals(first.getLeapSecondsWeekNum(), second.getLeapSecondsWeekNum());
295 Assertions.assertEquals(first.getLeapSecondsDayNum(), second.getLeapSecondsDayNum());
296 Assertions.assertEquals(first.getListAppliedDCBS().size(), second.getListAppliedDCBS().size());
297 for (int i = 0; i < first.getListAppliedDCBS().size(); ++i) {
298 checkDCB(first.getListAppliedDCBS().get(i), second.getListAppliedDCBS().get(i));
299 }
300 Assertions.assertEquals(first.getListAppliedPCVS().size(), second.getListAppliedPCVS().size());
301 for (int i = 0; i < first.getListAppliedPCVS().size(); ++i) {
302 checkPCV(first.getListAppliedPCVS().get(i), second.getListAppliedPCVS().get(i));
303 }
304 Assertions.assertEquals(first.getPhaseShiftCorrections().size(), second.getPhaseShiftCorrections().size());
305 for (int i = 0; i < first.getPhaseShiftCorrections().size(); ++i) {
306 checkPhaseShiftCorrection(first.getPhaseShiftCorrections().get(i), second.getPhaseShiftCorrections().get(i));
307 }
308 for (SatelliteSystem system : SatelliteSystem.values()) {
309 Assertions.assertEquals(first.getScaleFactorCorrections(system).size(), second.getScaleFactorCorrections(system).size());
310 for (int i = 0; i < first.getScaleFactorCorrections(system).size(); ++i) {
311 checkScaleFactorCorrection(first.getScaleFactorCorrections(system).get(i),
312 second.getScaleFactorCorrections(system).get(i));
313 }
314 }
315 Assertions.assertEquals(first.getGlonassChannels().size(), second.getGlonassChannels().size());
316 for (int i = 0; i < first.getGlonassChannels().size(); ++i) {
317 checkGlonassChannel(first.getGlonassChannels().get(i), second.getGlonassChannels().get(i));
318 }
319 Assertions.assertEquals(first.getNbSat(), second.getNbSat());
320 Assertions.assertEquals(first.getNbObsPerSat().size(), second.getNbObsPerSat().size());
321 for (final Map.Entry<SatInSystem, Map<ObservationType, Integer>> firstE : first.getNbObsPerSat().entrySet()) {
322 Map<ObservationType, Integer> firstV = firstE.getValue();
323 Map<ObservationType, Integer> secondV = second.getNbObsPerSat().get(firstE.getKey());
324 Assertions.assertEquals(firstV.size(), secondV.size());
325 for (final Map.Entry<ObservationType, Integer> firstF : firstV.entrySet()) {
326 Assertions.assertEquals(firstF.getValue(), secondV.get(firstF.getKey()));
327 }
328 }
329 Assertions.assertEquals(first.getTypeObs().size(), second.getTypeObs().size());
330 for (final Map.Entry<SatelliteSystem, List<ObservationType>> firstE : first.getTypeObs().entrySet()) {
331 List<ObservationType> firstT = firstE.getValue();
332 List<ObservationType> secondT = second.getTypeObs().get(firstE.getKey());
333 Assertions.assertEquals(firstT.size(), secondT.size());
334 for (int i = 0; i < firstT.size(); ++i) {
335 Assertions.assertEquals(firstT.get(i), secondT.get(i));
336 }
337 }
338 Assertions.assertTrue(Precision.equalsIncludingNaN(first.getC1cCodePhaseBias(), second.getC1cCodePhaseBias(), 1.0e-12));
339 Assertions.assertTrue(Precision.equalsIncludingNaN(first.getC1pCodePhaseBias(), second.getC1pCodePhaseBias(), 1.0e-12));
340 Assertions.assertTrue(Precision.equalsIncludingNaN(first.getC2cCodePhaseBias(), second.getC2cCodePhaseBias(), 1.0e-12));
341 Assertions.assertTrue(Precision.equalsIncludingNaN(first.getC2pCodePhaseBias(), second.getC2pCodePhaseBias(), 1.0e-12));
342
343 }
344
345 private void checkRinexComments(final RinexComment first, final RinexComment second) {
346 Assertions.assertEquals(first.getLineNumber(), second.getLineNumber());
347 Assertions.assertEquals(first.getText(), second.getText());
348 }
349
350 private void checkRinexObs(final ObservationDataSet first, final ObservationDataSet second,
351 final double expectedDt) {
352 Assertions.assertEquals(first.getSatellite().getSystem(), second.getSatellite().getSystem());
353 Assertions.assertEquals(first.getSatellite().getPRN(), second.getSatellite().getPRN());
354 checkDate(first.getDate(), second.getDate(), expectedDt);
355 Assertions.assertEquals(first.getEventFlag(), second.getEventFlag());
356 Assertions.assertEquals(first.getObservationData().size(), second.getObservationData().size());
357 for (int i = 0; i < first.getObservationData().size(); ++i) {
358 final ObservationData firstO = first.getObservationData().get(i);
359 final ObservationData secondO = second.getObservationData().get(i);
360 Assertions.assertEquals(firstO.getValue(), secondO.getValue(), 1.0e-12);
361 Assertions.assertEquals(firstO.getLossOfLockIndicator(), secondO.getLossOfLockIndicator());
362 Assertions.assertEquals(firstO.getSignalStrength(), secondO.getSignalStrength());
363 }
364 Assertions.assertTrue(Precision.equalsIncludingNaN(first.getRcvrClkOffset(), second.getRcvrClkOffset(), 1.0e-12));
365 }
366
367 private void checkDCB(final AppliedDCBS first, final AppliedDCBS second) {
368 Assertions.assertEquals(first.getSatelliteSystem(), second.getSatelliteSystem());
369 Assertions.assertEquals(first.getProgDCBS(), second.getProgDCBS());
370 Assertions.assertEquals(first.getSourceDCBS(), second.getSourceDCBS());
371 }
372
373 private void checkPCV(final AppliedPCVS first, final AppliedPCVS second) {
374 Assertions.assertEquals(first.getSatelliteSystem(), second.getSatelliteSystem());
375 Assertions.assertEquals(first.getProgPCVS(), second.getProgPCVS());
376 Assertions.assertEquals(first.getSourcePCVS(), second.getSourcePCVS());
377 }
378
379 private void checkPhaseShiftCorrection(final PhaseShiftCorrection first, final PhaseShiftCorrection second) {
380 Assertions.assertEquals(first.getSatelliteSystem(), second.getSatelliteSystem());
381 Assertions.assertEquals(first.getTypeObs(), second.getTypeObs());
382 Assertions.assertEquals(first.getCorrection(), second.getCorrection(), 1.0e-12);
383 Assertions.assertEquals(first.getSatsCorrected().size(), second.getSatsCorrected().size());
384 for (int i = 0; i < first.getSatsCorrected().size(); ++i) {
385 Assertions.assertEquals(first.getSatsCorrected().get(i).getSystem(), second.getSatsCorrected().get(i).getSystem());
386 Assertions.assertEquals(first.getSatsCorrected().get(i).getPRN(), second.getSatsCorrected().get(i).getPRN());
387 }
388 }
389
390 private void checkScaleFactorCorrection(final ScaleFactorCorrection first, final ScaleFactorCorrection second) {
391 Assertions.assertEquals(first.getCorrection(), second.getCorrection(), 1.0e-12);
392 Assertions.assertEquals(first.getTypesObsScaled().size(), second.getTypesObsScaled().size());
393 for (int i = 0; i < first.getTypesObsScaled().size(); ++i) {
394 Assertions.assertEquals(first.getTypesObsScaled().get(i), second.getTypesObsScaled().get(i));
395 }
396 }
397
398 private void checkGlonassChannel(final GlonassSatelliteChannel first, final GlonassSatelliteChannel second) {
399 Assertions.assertEquals(first.getSatellite().getSystem(), second.getSatellite().getSystem());
400 Assertions.assertEquals(first.getSatellite().getPRN(), second.getSatellite().getPRN());
401 Assertions.assertEquals(first.getK(), second.getK());
402 }
403
404 private void checkDate(final AbsoluteDate first, final AbsoluteDate second,
405 final double expectedDt) {
406 if (first == null) {
407 Assertions.assertNull(second);
408 } else if (Double.isInfinite(first.durationFrom(AbsoluteDate.ARBITRARY_EPOCH))) {
409 Assertions.assertEquals(first, second);
410 } else {
411 Assertions.assertEquals(expectedDt, second.durationFrom(first), 1.0e-6);
412 }
413 }
414
415 private void checkVector(final Vector3D first, final Vector3D second) {
416 if (first == null) {
417 Assertions.assertNull(second);
418 } else {
419 Assertions.assertEquals(0.0, Vector3D.distance(first, second), 1.0e-12 * first.getNorm());
420 }
421 }
422
423 }