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