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.IOException;
20 import java.util.ArrayList;
21 import java.util.Collections;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.function.BiFunction;
25
26 import org.hipparchus.geometry.euclidean.threed.Vector3D;
27 import org.hipparchus.util.FastMath;
28 import org.orekit.annotation.DefaultDataContext;
29 import org.orekit.data.DataContext;
30 import org.orekit.errors.OrekitException;
31 import org.orekit.errors.OrekitMessages;
32 import org.orekit.files.rinex.AppliedDCBS;
33 import org.orekit.files.rinex.AppliedPCVS;
34 import org.orekit.files.rinex.section.RinexComment;
35 import org.orekit.files.rinex.section.RinexLabels;
36 import org.orekit.gnss.ObservationTimeScale;
37 import org.orekit.gnss.ObservationType;
38 import org.orekit.gnss.PredefinedObservationType;
39 import org.orekit.gnss.SatInSystem;
40 import org.orekit.gnss.SatelliteSystem;
41 import org.orekit.time.AbsoluteDate;
42 import org.orekit.time.ClockModel;
43 import org.orekit.time.ClockTimeScale;
44 import org.orekit.time.DateTimeComponents;
45 import org.orekit.time.TimeScale;
46 import org.orekit.time.TimeScales;
47 import org.orekit.utils.formatting.FastDoubleFormatter;
48 import org.orekit.utils.formatting.FastLongFormatter;
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64 public class RinexObservationWriter implements AutoCloseable {
65
66
67 private static final int LABEL_INDEX = 60;
68
69
70 private static final FastLongFormatter ONE_DIGIT_INTEGER = new FastLongFormatter(1, false);
71
72
73 private static final FastLongFormatter PADDED_TWO_DIGITS_INTEGER = new FastLongFormatter(2, true);
74
75
76 private static final FastLongFormatter TWO_DIGITS_INTEGER = new FastLongFormatter(2, false);
77
78
79 private static final FastLongFormatter PADDED_FOUR_DIGITS_INTEGER = new FastLongFormatter(4, true);
80
81
82 private static final FastLongFormatter THREE_DIGITS_INTEGER = new FastLongFormatter(3, false);
83
84
85 private static final FastLongFormatter FOUR_DIGITS_INTEGER = new FastLongFormatter(4, false);
86
87
88 private static final FastLongFormatter SIX_DIGITS_INTEGER = new FastLongFormatter(6, false);
89
90
91 private static final FastDoubleFormatter EIGHT_THREE_DIGITS_FLOAT = new FastDoubleFormatter(8, 3);
92
93
94 private static final FastDoubleFormatter EIGHT_FIVE_DIGITS_FLOAT = new FastDoubleFormatter(8, 5);
95
96
97 private static final FastDoubleFormatter NINE_TWO_DIGITS_FLOAT = new FastDoubleFormatter(9, 2);
98
99
100 private static final FastDoubleFormatter NINE_FOUR_DIGITS_FLOAT = new FastDoubleFormatter(9, 4);
101
102
103 private static final FastDoubleFormatter TEN_THREE_DIGITS_FLOAT = new FastDoubleFormatter(10, 3);
104
105
106 private static final FastDoubleFormatter ELEVEN_SEVEN_DIGITS_FLOAT = new FastDoubleFormatter(11, 7);
107
108
109 private static final FastDoubleFormatter TWELVE_NINE_DIGITS_FLOAT = new FastDoubleFormatter(12, 9);
110
111
112 private static final FastDoubleFormatter THIRTEEN_SEVEN_DIGITS_FLOAT = new FastDoubleFormatter(13, 7);
113
114
115 private static final FastDoubleFormatter FOURTEEN_THREE_DIGITS_FLOAT = new FastDoubleFormatter(14, 3);
116
117
118 private static final FastDoubleFormatter FOURTEEN_FOUR_DIGITS_FLOAT = new FastDoubleFormatter(14, 4);
119
120
121 private static final FastDoubleFormatter FIFTEEN_TWELVE_DIGITS_FLOAT = new FastDoubleFormatter(15, 12);
122
123
124
125
126 private static final double EPS_DATE = 1.0e-8;
127
128
129 private final Appendable output;
130
131
132 private final String outputName;
133
134
135 private ClockModel receiverClockModel;
136
137
138 private TimeScale timeScale;
139
140
141 private RinexObservationHeader savedHeader;
142
143
144 private List<RinexComment> savedComments;
145
146
147 private final List<ObservationDataSet> pending;
148
149
150 private int lineNumber;
151
152
153 private int column;
154
155
156
157
158 private final TimeScales timeScales;
159
160
161
162
163 private final BiFunction<SatelliteSystem, TimeScales, ? extends TimeScale> timeScaleBuilder;
164
165
166
167
168
169
170
171
172
173
174
175 @DefaultDataContext
176 public RinexObservationWriter(final Appendable output, final String outputName) {
177 this(output, outputName,
178 (system, ts) -> system.getObservationTimeScale() == null ?
179 null :
180 system.getObservationTimeScale().getTimeScale(ts),
181 DataContext.getDefault().getTimeScales());
182 }
183
184
185
186
187
188
189
190
191 public RinexObservationWriter(final Appendable output, final String outputName,
192 final BiFunction<SatelliteSystem, TimeScales, ? extends TimeScale> timeScaleBuilder,
193 final TimeScales timeScales) {
194 this.output = output;
195 this.outputName = outputName;
196 this.savedHeader = null;
197 this.savedComments = Collections.emptyList();
198 this.pending = new ArrayList<>();
199 this.lineNumber = 0;
200 this.column = 0;
201 this.timeScaleBuilder = timeScaleBuilder;
202 this.timeScales = timeScales;
203 }
204
205
206 @Override
207 public void close() throws IOException {
208 processPending();
209 }
210
211
212
213
214
215 public void setReceiverClockModel(final ClockModel receiverClockModel) {
216 this.receiverClockModel = receiverClockModel;
217 }
218
219
220
221
222
223
224
225
226
227
228
229
230
231 @DefaultDataContext
232 public void writeCompleteFile(final RinexObservation rinexObservation)
233 throws IOException {
234 prepareComments(rinexObservation.getComments());
235 writeHeader(rinexObservation.getHeader());
236 for (final ObservationDataSet observationDataSet : rinexObservation.getObservationDataSets()) {
237 writeObservationDataSet(observationDataSet);
238 }
239 }
240
241
242
243
244 public void prepareComments(final List<RinexComment> comments) {
245 savedComments = comments;
246 }
247
248
249
250
251
252
253
254
255
256 @DefaultDataContext
257 public void writeHeader(final RinexObservationHeader header)
258 throws IOException {
259
260
261 if (savedHeader != null) {
262 throw new OrekitException(OrekitMessages.HEADER_ALREADY_WRITTEN, outputName);
263 }
264 savedHeader = header;
265 lineNumber = 1;
266
267 final String timeScaleName;
268 if (timeScaleBuilder.apply(header.getSatelliteSystem(), timeScales) != null) {
269 timeScale = timeScaleBuilder.apply(header.getSatelliteSystem(), timeScales);
270 timeScaleName = " ";
271 } else {
272 timeScale = ObservationTimeScale.GPS.getTimeScale(timeScales);
273 timeScaleName = timeScale.getName();
274 }
275 if (!header.getClockOffsetApplied() && receiverClockModel != null) {
276
277
278
279
280 timeScale = new ClockTimeScale(timeScale.getName(), timeScale, receiverClockModel);
281 }
282
283
284 outputField(NINE_TWO_DIGITS_FLOAT, header.getFormatVersion(), 9);
285 outputField("", 20, true);
286 outputField("OBSERVATION DATA", 40, true);
287 outputField(header.getSatelliteSystem().getKey(), 41);
288 finishHeaderLine(RinexLabels.VERSION);
289
290
291 outputField(header.getProgramName(), 20, true);
292 outputField(header.getRunByName(), 40, true);
293 final DateTimeComponents dtc = header.getCreationDateComponents();
294 if (header.getFormatVersion() < 3.0 && dtc.getTime().getSecond() < 0.5) {
295 outputField(PADDED_TWO_DIGITS_INTEGER, dtc.getDate().getDay(), 42);
296 outputField('-', 43);
297 outputField(dtc.getDate().getMonthEnum().getUpperCaseAbbreviation(), 46, true);
298 outputField('-', 47);
299 outputField(PADDED_TWO_DIGITS_INTEGER, dtc.getDate().getYear() % 100, 49);
300 outputField(PADDED_TWO_DIGITS_INTEGER, dtc.getTime().getHour(), 52);
301 outputField(':', 53);
302 outputField(PADDED_TWO_DIGITS_INTEGER, dtc.getTime().getMinute(), 55);
303 outputField(header.getCreationTimeZone(), 58, true);
304 } else {
305 outputField(PADDED_FOUR_DIGITS_INTEGER, dtc.getDate().getYear(), 44);
306 outputField(PADDED_TWO_DIGITS_INTEGER, dtc.getDate().getMonth(), 46);
307 outputField(PADDED_TWO_DIGITS_INTEGER, dtc.getDate().getDay(), 48);
308 outputField(PADDED_TWO_DIGITS_INTEGER, dtc.getTime().getHour(), 51);
309 outputField(PADDED_TWO_DIGITS_INTEGER, dtc.getTime().getMinute(), 53);
310 outputField(PADDED_TWO_DIGITS_INTEGER, (int) FastMath.rint(dtc.getTime().getSecond()), 55);
311 outputField(header.getCreationTimeZone(), 59, false);
312 }
313 finishHeaderLine(RinexLabels.PROGRAM);
314
315
316 outputField(header.getMarkerName(), 60, true);
317 finishHeaderLine(RinexLabels.MARKER_NAME);
318
319
320 if (header.getMarkerNumber() != null) {
321 outputField(header.getMarkerNumber(), 20, true);
322 finishHeaderLine(RinexLabels.MARKER_NUMBER);
323 }
324
325
326 if (header.getFormatVersion() >= 2.20) {
327 outputField(header.getMarkerType(), 20, true);
328 finishHeaderLine(RinexLabels.MARKER_TYPE);
329 }
330
331
332 outputField(header.getObserverName(), 20, true);
333 outputField(header.getAgencyName(), 60, true);
334 finishHeaderLine(RinexLabels.OBSERVER_AGENCY);
335
336
337 outputField(header.getReceiverNumber(), 20, true);
338 outputField(header.getReceiverType(), 40, true);
339 outputField(header.getReceiverVersion(), 60, true);
340 finishHeaderLine(RinexLabels.REC_NB_TYPE_VERS);
341
342
343 outputField(header.getAntennaNumber(), 20, true);
344 outputField(header.getAntennaType(), 40, true);
345 finishHeaderLine(RinexLabels.ANT_NB_TYPE);
346
347
348 writeHeaderLine(header.getApproxPos(), RinexLabels.APPROX_POSITION_XYZ);
349
350
351 if (!Double.isNaN(header.getAntennaHeight())) {
352 outputField(FOURTEEN_FOUR_DIGITS_FLOAT, header.getAntennaHeight(), 14);
353 outputField(FOURTEEN_FOUR_DIGITS_FLOAT, header.getEccentricities().getX(), 28);
354 outputField(FOURTEEN_FOUR_DIGITS_FLOAT, header.getEccentricities().getY(), 42);
355 finishHeaderLine(RinexLabels.ANTENNA_DELTA_H_E_N);
356 }
357
358
359 writeHeaderLine(header.getAntennaReferencePoint(), RinexLabels.ANTENNA_DELTA_X_Y_Z);
360
361
362 if (header.getAntennaPhaseCenter() != null) {
363 outputField(header.getPhaseCenterSystem().getKey(), 1);
364 outputField("", 2, true);
365 outputField(header.getObservationCode(), 5, true);
366 outputField(NINE_FOUR_DIGITS_FLOAT, header.getAntennaPhaseCenter().getX(), 14);
367 outputField(FOURTEEN_FOUR_DIGITS_FLOAT, header.getAntennaPhaseCenter().getY(), 28);
368 outputField(FOURTEEN_FOUR_DIGITS_FLOAT, header.getAntennaPhaseCenter().getZ(), 42);
369 finishHeaderLine(RinexLabels.ANTENNA_PHASE_CENTER);
370 }
371
372
373 writeHeaderLine(header.getAntennaBSight(), RinexLabels.ANTENNA_B_SIGHT_XYZ);
374
375
376 if (!Double.isNaN(header.getAntennaAzimuth())) {
377 outputField(FOURTEEN_FOUR_DIGITS_FLOAT, FastMath.toDegrees(header.getAntennaAzimuth()), 14);
378 finishHeaderLine(RinexLabels.ANTENNA_ZERODIR_AZI);
379 }
380
381
382 writeHeaderLine(header.getAntennaZeroDirection(), RinexLabels.ANTENNA_ZERODIR_XYZ);
383
384
385 if (FastMath.abs(header.getFormatVersion() - 2.20) < 0.001) {
386 for (final SatelliteSystem system : SatelliteSystem.values()) {
387 for (final ScaleFactorCorrection sfc : header.getScaleFactorCorrections(system)) {
388 if (sfc != null) {
389 outputField(SIX_DIGITS_INTEGER, (int) FastMath.round(sfc.getCorrection()), 6);
390 outputField(SIX_DIGITS_INTEGER, sfc.getTypesObsScaled().size(), 12);
391 for (int i = 0; i < sfc.getTypesObsScaled().size(); ++i) {
392 outputField(sfc.getTypesObsScaled().get(i).getName(), 18 + 6 * i, false);
393 }
394 finishHeaderLine(RinexLabels.OBS_SCALE_FACTOR);
395 }
396 }
397 }
398 }
399
400
401 writeHeaderLine(header.getCenterMass(), RinexLabels.CENTER_OF_MASS_XYZ);
402
403
404 writeHeaderLine(header.getDoi(), RinexLabels.DOI);
405
406
407 writeHeaderLine(header.getLicense(), RinexLabels.LICENSE);
408
409
410 writeHeaderLine(header.getStationInformation(), RinexLabels.STATION_INFORMATION);
411
412
413 for (Map.Entry<SatelliteSystem, List<ObservationType>> entry : header.getTypeObs().entrySet()) {
414 if (header.getFormatVersion() < 3.0) {
415 outputField(SIX_DIGITS_INTEGER, entry.getValue().size(), 6);
416 } else {
417 outputField(entry.getKey().getKey(), 1);
418 outputField(THREE_DIGITS_INTEGER, entry.getValue().size(), 6);
419 }
420 for (final ObservationType observationType : entry.getValue()) {
421 int next = column + (header.getFormatVersion() < 3.0 ? 6 : 4);
422 if (next > LABEL_INDEX) {
423
424 finishHeaderLine(header.getFormatVersion() < 3.0 ?
425 RinexLabels.NB_TYPES_OF_OBSERV :
426 RinexLabels.SYS_NB_TYPES_OF_OBSERV);
427 outputField("", 6, true);
428 next = column + (header.getFormatVersion() < 3.0 ? 6 : 4);
429 }
430 outputField(observationType.getName(), next, false);
431 }
432 finishHeaderLine(header.getFormatVersion() < 3.0 ?
433 RinexLabels.NB_TYPES_OF_OBSERV :
434 RinexLabels.SYS_NB_TYPES_OF_OBSERV);
435 }
436
437
438 writeHeaderLine(header.getSignalStrengthUnit(), RinexLabels.SIGNAL_STRENGTH_UNIT);
439
440
441 if (!Double.isNaN(header.getInterval())) {
442 outputField(TEN_THREE_DIGITS_FLOAT, header.getInterval(), 10);
443 finishHeaderLine(RinexLabels.INTERVAL);
444 }
445
446
447 final DateTimeComponents dtcFirst = header.getTFirstObs().getComponents(timeScale).roundIfNeeded(60, 7);
448 outputField(SIX_DIGITS_INTEGER, dtcFirst.getDate().getYear(), 6);
449 outputField(SIX_DIGITS_INTEGER, dtcFirst.getDate().getMonth(), 12);
450 outputField(SIX_DIGITS_INTEGER, dtcFirst.getDate().getDay(), 18);
451 outputField(SIX_DIGITS_INTEGER, dtcFirst.getTime().getHour(), 24);
452 outputField(SIX_DIGITS_INTEGER, dtcFirst.getTime().getMinute(), 30);
453 outputField(THIRTEEN_SEVEN_DIGITS_FLOAT, dtcFirst.getTime().getSecond(), 43);
454 outputField(timeScaleName, 51, false);
455 finishHeaderLine(RinexLabels.TIME_OF_FIRST_OBS);
456
457
458 if (!header.getTLastObs().equals(AbsoluteDate.FUTURE_INFINITY)) {
459 final DateTimeComponents dtcLast = header.getTLastObs().getComponents(timeScale).roundIfNeeded(60, 7);
460 outputField(SIX_DIGITS_INTEGER, dtcLast.getDate().getYear(), 6);
461 outputField(SIX_DIGITS_INTEGER, dtcLast.getDate().getMonth(), 12);
462 outputField(SIX_DIGITS_INTEGER, dtcLast.getDate().getDay(), 18);
463 outputField(SIX_DIGITS_INTEGER, dtcLast.getTime().getHour(), 24);
464 outputField(SIX_DIGITS_INTEGER, dtcLast.getTime().getMinute(), 30);
465 outputField(THIRTEEN_SEVEN_DIGITS_FLOAT, dtcLast.getTime().getSecond(), 43);
466 outputField(timeScaleName, 51, false);
467 finishHeaderLine(RinexLabels.TIME_OF_LAST_OBS);
468 }
469
470
471 outputField(SIX_DIGITS_INTEGER, header.getClockOffsetApplied() ? 1 : 0, 6);
472 finishHeaderLine(RinexLabels.RCV_CLOCK_OFFS_APPL);
473
474
475 for (final AppliedDCBS appliedDCBS : header.getListAppliedDCBS()) {
476 outputField(appliedDCBS.getSatelliteSystem().getKey(), 1);
477 outputField("", 2, true);
478 outputField(appliedDCBS.getProgDCBS(), 20, true);
479 outputField(appliedDCBS.getSourceDCBS(), 60, true);
480 finishHeaderLine(RinexLabels.SYS_DCBS_APPLIED);
481 }
482
483
484 for (final AppliedPCVS appliedPCVS : header.getListAppliedPCVS()) {
485 outputField(appliedPCVS.getSatelliteSystem().getKey(), 1);
486 outputField("", 2, true);
487 outputField(appliedPCVS.getProgPCVS(), 20, true);
488 outputField(appliedPCVS.getSourcePCVS(), 60, true);
489 finishHeaderLine(RinexLabels.SYS_PCVS_APPLIED);
490 }
491
492
493 if (header.getFormatVersion() >= 3.0) {
494 for (final SatelliteSystem system : SatelliteSystem.values()) {
495 for (final ScaleFactorCorrection sfc : header.getScaleFactorCorrections(system)) {
496 if (sfc != null) {
497 outputField(system.getKey(), 1);
498 outputField("", 2, true);
499 outputField(FOUR_DIGITS_INTEGER, (int) FastMath.rint(sfc.getCorrection()), 6);
500 if (sfc.getTypesObsScaled().size() < header.getTypeObs().get(system).size()) {
501 outputField("", 8, true);
502 outputField(TWO_DIGITS_INTEGER, sfc.getTypesObsScaled().size(), 10);
503 for (ObservationType observationType : sfc.getTypesObsScaled()) {
504 int next = column + 4;
505 if (next > LABEL_INDEX) {
506
507 finishHeaderLine(RinexLabels.SYS_SCALE_FACTOR);
508 outputField("", 10, true);
509 next = column + 4;
510 }
511 outputField("", next - 3, true);
512 outputField(observationType.getName(), next, true);
513 }
514 }
515 finishHeaderLine(RinexLabels.SYS_SCALE_FACTOR);
516 }
517 }
518 }
519 }
520
521
522 for (final PhaseShiftCorrection psc : header.getPhaseShiftCorrections()) {
523 outputField(psc.getSatelliteSystem().getKey(), 1);
524 outputField(psc.getTypeObs().getName(), 5, false);
525 outputField(EIGHT_FIVE_DIGITS_FLOAT, psc.getCorrection(), 14);
526 if (!psc.getSatsCorrected().isEmpty()) {
527 outputField(TWO_DIGITS_INTEGER, psc.getSatsCorrected().size(), 18);
528 for (final SatInSystem sis : psc.getSatsCorrected()) {
529 int next = column + 4;
530 if (next > LABEL_INDEX) {
531
532 finishHeaderLine(RinexLabels.SYS_PHASE_SHIFT);
533 outputField("", 18, true);
534 next = column + 4;
535 }
536 outputField(sis.toString(), next, false);
537 }
538 }
539 finishHeaderLine(RinexLabels.SYS_PHASE_SHIFT);
540 }
541
542 if (header.getFormatVersion() >= 3.01) {
543 if (!header.getGlonassChannels().isEmpty()) {
544
545 outputField(THREE_DIGITS_INTEGER, header.getGlonassChannels().size(), 3);
546 outputField("", 4, true);
547 for (final GlonassSatelliteChannel channel : header.getGlonassChannels()) {
548 int next = column + 7;
549 if (next > LABEL_INDEX) {
550
551 finishHeaderLine(RinexLabels.GLONASS_SLOT_FRQ_NB);
552 outputField("", 4, true);
553 next = column + 7;
554 }
555 outputField(channel.getSatellite().getSystem().getKey(), next - 6);
556 outputField(PADDED_TWO_DIGITS_INTEGER, channel.getSatellite().getPRN(), next - 4);
557 outputField(TWO_DIGITS_INTEGER, channel.getK(), next - 1);
558 outputField("", next, true);
559 }
560 }
561 finishHeaderLine(RinexLabels.GLONASS_SLOT_FRQ_NB);
562 }
563
564 if (header.getFormatVersion() >= 3.0) {
565
566 if (Double.isNaN(header.getC1cCodePhaseBias())) {
567 outputField("", 13, true);
568 } else {
569 outputField(PredefinedObservationType.C1C.getName(), 4, false);
570 outputField("", 5, true);
571 outputField(EIGHT_THREE_DIGITS_FLOAT, header.getC1cCodePhaseBias(), 13);
572 }
573 if (Double.isNaN(header.getC1pCodePhaseBias())) {
574 outputField("", 26, true);
575 } else {
576 outputField(PredefinedObservationType.C1P.getName(), 17, false);
577 outputField("", 18, true);
578 outputField(EIGHT_THREE_DIGITS_FLOAT, header.getC1pCodePhaseBias(), 26);
579 }
580 if (Double.isNaN(header.getC2cCodePhaseBias())) {
581 outputField("", 39, true);
582 } else {
583 outputField(PredefinedObservationType.C2C.getName(), 30, false);
584 outputField("", 31, true);
585 outputField(EIGHT_THREE_DIGITS_FLOAT, header.getC2cCodePhaseBias(), 39);
586 }
587 if (Double.isNaN(header.getC2pCodePhaseBias())) {
588 outputField("", 52, true);
589 } else {
590 outputField(PredefinedObservationType.C2P.getName(), 43, false);
591 outputField("", 44, true);
592 outputField(EIGHT_THREE_DIGITS_FLOAT, header.getC2pCodePhaseBias(), 52);
593 }
594 finishHeaderLine(RinexLabels.GLONASS_COD_PHS_BIS);
595 }
596
597
598 if (header.getLeapSeconds() > 0) {
599 outputField(SIX_DIGITS_INTEGER, header.getLeapSeconds(), 6);
600 if (header.getFormatVersion() >= 3.0) {
601 outputField(SIX_DIGITS_INTEGER, header.getLeapSecondsFuture(), 12);
602 outputField(SIX_DIGITS_INTEGER, header.getLeapSecondsWeekNum(), 18);
603 outputField(SIX_DIGITS_INTEGER, header.getLeapSecondsDayNum(), 24);
604 }
605 finishHeaderLine(RinexLabels.LEAP_SECONDS);
606 }
607
608
609 if (header.getNbSat() >= 0) {
610 outputField(SIX_DIGITS_INTEGER, header.getNbSat(), 6);
611 finishHeaderLine(RinexLabels.NB_OF_SATELLITES);
612 }
613
614
615 for (final Map.Entry<SatInSystem, Map<ObservationType, Integer>> entry1 : header.getNbObsPerSat().entrySet()) {
616 final SatInSystem sis = entry1.getKey();
617 outputField(sis.toString(), 6, false);
618
619 for (final ObservationType obsType : header.getTypeObs().get(sis.getSystem())) {
620 final Integer nbObs = entry1.getValue().get(obsType);
621 int next = column + 6;
622 if (next > LABEL_INDEX) {
623
624 finishHeaderLine(RinexLabels.PRN_NB_OF_OBS);
625 outputField("", 6, true);
626 next = column + 6;
627 }
628 outputField(SIX_DIGITS_INTEGER, nbObs == null ? 0 : nbObs, next);
629 }
630 finishHeaderLine(RinexLabels.PRN_NB_OF_OBS);
631 }
632
633
634 writeHeaderLine("", RinexLabels.END);
635
636 }
637
638
639
640
641
642
643
644
645
646 public void writeObservationDataSet(final ObservationDataSet observationDataSet)
647 throws IOException {
648
649
650 if (savedHeader == null) {
651 throw new OrekitException(OrekitMessages.HEADER_NOT_WRITTEN, outputName);
652 }
653
654 if (!pending.isEmpty() && observationDataSet.durationFrom(pending.get(0).getDate()) > EPS_DATE) {
655
656
657 processPending();
658 }
659
660
661 pending.add(observationDataSet);
662
663 }
664
665
666
667
668 private void processPending() throws IOException {
669
670 if (!pending.isEmpty()) {
671
672
673 if (savedHeader.getFormatVersion() < 3.0) {
674 writePendingRinex2Observations();
675 } else {
676 writePendingRinex34Observations();
677 }
678
679
680 pending.clear();
681
682 }
683
684 }
685
686
687
688
689 public void writePendingRinex2Observations() throws IOException {
690
691 final ObservationDataSet first = pending.get(0);
692
693
694 final DateTimeComponents dtc = first.getDate().getComponents(timeScale).roundIfNeeded(60, 7);
695 outputField("", 1, true);
696 outputField(PADDED_TWO_DIGITS_INTEGER, dtc.getDate().getYear() % 100, 3);
697 outputField("", 4, true);
698 outputField(TWO_DIGITS_INTEGER, dtc.getDate().getMonth(), 6);
699 outputField("", 7, true);
700 outputField(TWO_DIGITS_INTEGER, dtc.getDate().getDay(), 9);
701 outputField("", 10, true);
702 outputField(TWO_DIGITS_INTEGER, dtc.getTime().getHour(), 12);
703 outputField("", 13, true);
704 outputField(TWO_DIGITS_INTEGER, dtc.getTime().getMinute(), 15);
705 outputField(ELEVEN_SEVEN_DIGITS_FLOAT, dtc.getTime().getSecond(), 26);
706
707
708 outputField("", 28, true);
709 outputField(ONE_DIGIT_INTEGER, first.getEventFlag(), 29);
710
711
712 outputField(THREE_DIGITS_INTEGER, pending.size(), 32);
713 boolean offsetWritten = false;
714 final double clockOffset = first.getRcvrClkOffset();
715 for (final ObservationDataSet ods : pending) {
716 int next = column + 3;
717 if (next > 68) {
718
719 if (clockOffset != 0.0) {
720 outputField(TWELVE_NINE_DIGITS_FLOAT, clockOffset, 80);
721 }
722 offsetWritten = true;
723 finishLine();
724 outputField("", 32, true);
725 next = column + 3;
726 }
727 outputField(ods.getSatellite().toString(), next, false);
728 }
729 if (!offsetWritten && clockOffset != 0.0) {
730 outputField("", 68, true);
731 outputField(TWELVE_NINE_DIGITS_FLOAT, first.getRcvrClkOffset(), 80);
732 }
733 finishLine();
734
735
736 for (final ObservationDataSet ods : pending) {
737 for (final ObservationData od : ods.getObservationData()) {
738 int next = column + 16;
739 if (next > 80) {
740
741 finishLine();
742 next = column + 16;
743 }
744 final double scaling = getScaling(od.getObservationType(), ods.getSatellite().getSystem());
745 outputField(FOURTEEN_THREE_DIGITS_FLOAT, scaling * od.getValue(), next - 2);
746 if (od.getLossOfLockIndicator() == 0) {
747 outputField("", next - 1, true);
748 } else {
749 outputField(ONE_DIGIT_INTEGER, od.getLossOfLockIndicator(), next - 1);
750 }
751 if (od.getSignalStrength() == 0) {
752 outputField("", next, true);
753 } else {
754 outputField(ONE_DIGIT_INTEGER, od.getSignalStrength(), next);
755 }
756 }
757 finishLine();
758 }
759
760 }
761
762
763
764
765 public void writePendingRinex34Observations()
766 throws IOException {
767
768 final ObservationDataSet first = pending.get(0);
769
770
771 final DateTimeComponents dtc = first.getDate().getComponents(timeScale).roundIfNeeded(60, 7);
772 outputField(">", 2, true);
773 outputField(FOUR_DIGITS_INTEGER, dtc.getDate().getYear(), 6);
774 outputField("", 7, true);
775 outputField(PADDED_TWO_DIGITS_INTEGER, dtc.getDate().getMonth(), 9);
776 outputField("", 10, true);
777 outputField(PADDED_TWO_DIGITS_INTEGER, dtc.getDate().getDay(), 12);
778 outputField("", 13, true);
779 outputField(PADDED_TWO_DIGITS_INTEGER, dtc.getTime().getHour(), 15);
780 outputField("", 16, true);
781 outputField(PADDED_TWO_DIGITS_INTEGER, dtc.getTime().getMinute(), 18);
782 outputField(ELEVEN_SEVEN_DIGITS_FLOAT, dtc.getTime().getSecond(), 29);
783
784
785 outputField("", 31, true);
786 outputField(ONE_DIGIT_INTEGER, first.getEventFlag(), 32);
787
788
789 outputField(THREE_DIGITS_INTEGER, pending.size(), 35);
790 if (first.getRcvrClkOffset() != 0.0) {
791 outputField("", 41, true);
792 outputField(FIFTEEN_TWELVE_DIGITS_FLOAT, first.getRcvrClkOffset(), 56);
793 }
794 finishLine();
795
796
797 for (final ObservationDataSet ods : pending) {
798 outputField(ods.getSatellite().toString(), 3, false);
799 for (final ObservationData od : ods.getObservationData()) {
800 final int next = column + 16;
801 final double scaling = getScaling(od.getObservationType(), ods.getSatellite().getSystem());
802 outputField(FOURTEEN_THREE_DIGITS_FLOAT, scaling * od.getValue(), next - 2);
803 if (od.getLossOfLockIndicator() == 0) {
804 outputField("", next - 1, true);
805 } else {
806 outputField(ONE_DIGIT_INTEGER, od.getLossOfLockIndicator(), next - 1);
807 }
808 if (od.getSignalStrength() == 0) {
809 outputField("", next, true);
810 } else {
811 outputField(ONE_DIGIT_INTEGER, od.getSignalStrength(), next);
812 }
813 }
814 finishLine();
815 }
816
817 }
818
819
820
821
822
823
824 private void writeHeaderLine(final String s, final RinexLabels label) throws IOException {
825 if (s != null) {
826 outputField(s, s.length(), true);
827 finishHeaderLine(label);
828 }
829 }
830
831
832
833
834
835
836 private void writeHeaderLine(final Vector3D vector, final RinexLabels label) throws IOException {
837 if (vector != null) {
838 outputField(FOURTEEN_FOUR_DIGITS_FLOAT, vector.getX(), 14);
839 outputField(FOURTEEN_FOUR_DIGITS_FLOAT, vector.getY(), 28);
840 outputField(FOURTEEN_FOUR_DIGITS_FLOAT, vector.getZ(), 42);
841 finishHeaderLine(label);
842 }
843 }
844
845
846
847
848
849 private void finishHeaderLine(final RinexLabels label) throws IOException {
850 for (int i = column; i < LABEL_INDEX; ++i) {
851 output.append(' ');
852 }
853 output.append(label.getLabel());
854 finishLine();
855 }
856
857
858
859
860 private void finishLine() throws IOException {
861
862
863 output.append(System.lineSeparator());
864 lineNumber++;
865 column = 0;
866
867
868 for (final RinexComment comment : savedComments) {
869 if (comment.getLineNumber() == lineNumber) {
870 outputField(comment.getText(), LABEL_INDEX, true);
871 output.append(RinexLabels.COMMENT.getLabel());
872 output.append(System.lineSeparator());
873 lineNumber++;
874 column = 0;
875 } else if (comment.getLineNumber() > lineNumber) {
876 break;
877 }
878 }
879
880 }
881
882
883
884
885
886
887 private void outputField(final char c, final int next) throws IOException {
888 outputField(Character.toString(c), next, false);
889 }
890
891
892
893
894
895
896
897 private void outputField(final FastLongFormatter formatter, final int value, final int next) throws IOException {
898 outputField(formatter.toString(value), next, false);
899 }
900
901
902
903
904
905
906
907 private void outputField(final FastDoubleFormatter formatter, final double value, final int next) throws IOException {
908 if (Double.isNaN(value)) {
909
910 outputField("", next, true);
911 } else {
912 outputField(formatter.toString(value), next, false);
913 }
914 }
915
916
917
918
919
920
921
922 private void outputField(final String field, final int next, final boolean leftJustified) throws IOException {
923 final int padding = next - (field == null ? 0 : field.length()) - column;
924 if (padding < 0) {
925 throw new OrekitException(OrekitMessages.FIELD_TOO_LONG, field, next - column);
926 }
927 if (leftJustified && field != null) {
928 output.append(field);
929 }
930 for (int i = 0; i < padding; ++i) {
931 output.append(' ');
932 }
933 if (!leftJustified && field != null) {
934 output.append(field);
935 }
936 column = next;
937 }
938
939
940
941
942
943
944 private double getScaling(final ObservationType type, final SatelliteSystem system) {
945
946 for (final ScaleFactorCorrection scaleFactorCorrection : savedHeader.getScaleFactorCorrections(system)) {
947
948 if (scaleFactorCorrection.getTypesObsScaled().contains(type)) {
949 return scaleFactorCorrection.getCorrection();
950 }
951 }
952
953
954 return 1.0;
955
956 }
957
958 }