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