1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
//! Observation RINEX parsing
use crate::{
epoch::{parse_in_timescale as parse_epoch_in_timescale, parse_utc as parse_utc_epoch},
observation::{
ClockObservation, EpochFlag, LliFlags, ObsKey, Observations, SignalObservation, SNR,
},
parse_f64,
prelude::{Constellation, Header, Observable, ParsingError, TimeScale, Version, SV},
};
use std::{
collections::HashMap,
str::{FromStr, Lines},
};
// TODO: see if we can get rid of num_integer
use num_integer::div_ceil;
// #[cfg(feature = "log")]
// use log::{debug, error};
/// Returns true if provided content matches the start of a new Observation Epoch
pub fn is_new_epoch(line: &str, v: Version) -> bool {
if v.major < 3 {
if line.len() < 30 {
false
} else {
// SPLICE flag handling (still an Observation::flag)
let significant = !line[0..26].trim().is_empty();
let epoch = parse_utc_epoch(&line[0..26]);
let flag = EpochFlag::from_str(line[26..29].trim());
if significant {
epoch.is_ok() && flag.is_ok()
} else if flag.is_err() {
false
} else {
match flag.unwrap() {
EpochFlag::AntennaBeingMoved
| EpochFlag::NewSiteOccupation
| EpochFlag::HeaderInformationFollows
| EpochFlag::ExternalEvent => true,
_ => false,
}
}
}
} else {
// Modern RINEX has a simple marker, like all V4 modern files
match line.chars().next() {
Some(c) => {
c == '>' // epochs always delimited
// by this new identifier
},
_ => false,
}
}
}
/// Parses record entries from readable content
/// ## Input
/// - header: reference to previously parsed [Header]
/// - content: readable content
/// - ts: [TimeScale] defined by [Header]
/// - observations: preallocated [Observations] for performance issue.
/// ## Output
/// - [ObsKey] record indexer
pub fn parse_epoch(
header: &Header,
content: &str,
ts: TimeScale,
observations: &mut Observations,
) -> Result<ObsKey, ParsingError> {
let mut lines = content.lines();
let mut line = match lines.next() {
Some(l) => l,
_ => return Err(ParsingError::EmptyEpoch),
};
// epoch::
let mut offset: usize = 2+1 // Y
+2+1 // d
+2+1 // m
+2+1 // h
+2+1 // m
+11; // secs
// V > 2 epoch::year is a 4 digit number
if header.version.major > 2 {
offset += 2
}
// V > 2 might start with a ">" marker
if line.starts_with('>') {
line = line.split_at(1).1;
}
let (date, rem) = line.split_at(offset);
let epoch = parse_epoch_in_timescale(date, ts)?;
let (flag, rem) = rem.split_at(3);
let flag = EpochFlag::from_str(flag.trim())?;
let key = ObsKey { epoch, flag };
let (num_sat, rem) = rem.split_at(3);
let num_sat = num_sat
.trim()
.parse::<u16>()
.map_err(|_| ParsingError::NumSatParsing)?;
// grab possible clock offset
let offs: Option<&str> = match header.version.major < 2 {
true => {
// RINEX 2
// clock offsets are last 12 characters
if line.len() > 60 - 12 {
Some(line.split_at(60 - 12).1.trim())
} else {
None
}
},
false => {
// RINEX 3
let min_len: usize = 4+1 // y
+2+1 // m
+2+1 // d
+2+1 // h
+2+1 // m
+11+1// s
+3 // flag
+3; // n_sat
if line.len() > min_len {
// RINEX3: clock offset precision was increased
Some(line.split_at(min_len).1.trim()) // this handles it naturally
} else {
None
}
},
};
if let Some(offset) = offs {
if let Ok(offset_s) = parse_f64(offset) {
observations
.with_clock_observation(ClockObservation::default().with_offset_s(epoch, offset_s));
}
}
match flag {
EpochFlag::Ok | EpochFlag::PowerFailure | EpochFlag::CycleSlip => {
parse_observations(header, num_sat, rem, lines, &mut observations.signals)?;
},
_ => {
// Hardware events are not supported yet (coming soon)
return Err(ParsingError::ObsHardwareEvent);
},
}
Ok(key)
}
fn parse_observations(
header: &Header,
num_sat: u16,
rem: &str,
mut lines: Lines<'_>,
signals: &mut Vec<SignalObservation>,
) -> Result<(), ParsingError> {
// retrieve header specs
let constellation = header.constellation;
let obs = header.obs.as_ref().unwrap();
let observables = &obs.codes;
// V1 / V2 tedious case
let rem = rem.trim();
let remainder_len = rem.len();
if header.version.major < 3 {
// Sets the satellite systems description, which consits in
// - end of current line
// - possible following lines
// starts with first line content
let end = (12 * 3).min(remainder_len);
let mut systems_str = rem[..end].to_string();
let mut systems_str_len = systems_str.len();
// grab following lines (if we need to)
while systems_str_len / 3 < num_sat.into() {
if let Some(line) = lines.next() {
let trimmed = line.trim();
systems_str_len += trimmed.len();
systems_str.push_str(trimmed);
} else {
return Err(ParsingError::BadV2SatellitesDescription);
}
}
let systems_str_len = systems_str.len();
parse_signals_v2(
&systems_str,
systems_str_len,
constellation,
observables,
lines,
signals,
);
} else {
parse_signals_v3(observables, lines, signals);
}
Ok(())
}
/// Parses all [SignalObservation]s as described by following V2 content.
/// Old format is tedious:
/// - vehicle description is contained in first line
/// - wrapped in as many lines as needed
/// Inputs
/// - system_str: first line description
/// - constellation: [Constellation] specs defined in [Header]
/// - observables: reference to [Observable]s specs defined in [Header]
/// - lines: remaing [Lines] Iterator
fn parse_signals_v2(
systems_str: &str,
systems_str_len: usize,
head_constellation: Option<Constellation>,
head_observables: &HashMap<Constellation, Vec<Observable>>,
lines: Lines<'_>,
signals: &mut Vec<SignalObservation>,
) {
const SVNN_SIZE: usize = 3; // SVNN standard
const MAX_OBSERVABLES_LINE: usize = 5; // max in a single line
const OBSERVABLE_F14_WIDTH: usize = 14;
const OBSERVABLE_WIDTH: usize = OBSERVABLE_F14_WIDTH + 2; // data +lli +snr +1separator
// basic check that avoid entering the loop for nothing
if systems_str_len < SVNN_SIZE {
// does not look good (=rubbish first line)
//#[cfg(feature = "log")]
//error!("parse_sig_v2(aborted): empty content");
return;
}
// sv pointer
let mut sv = SV::default();
let mut sv_ptr = 0;
let mut sv_identified = false;
// let numsat = systems_str_len / SVNN_SIZE;
// observable pointer
let mut obs_ptr = 0;
let mut obs_identified = false;
let mut observables = &Vec::<Observable>::default();
// browse all lines
for line in lines {
// [SV] identification
// 1. on first line (not defined yet!)
// 2. every time one SV is concluded
if !sv_identified {
// identify new SV
let sv_end = (sv_ptr + SVNN_SIZE).min(systems_str_len);
let system = &systems_str[sv_ptr..sv_end].trim();
// actual parsing
if let Ok(found) = SV::from_str(system) {
sv = found;
} else {
// This may fail on very old RINEX mono GNSS
// that omit the constellation in the description
match head_constellation {
Some(Constellation::Mixed) | None => {
//#[cfg(feature = "log")]
//error!("parse_sig_v2(abort): no constell specs");
break;
},
Some(gnss) => {
if let Ok(prn) = system.trim().parse::<u8>() {
sv = SV {
prn,
constellation: gnss,
};
} else {
//#[cfg(feature = "log")]
//error!("parse_sig_v2(abort): invalid sv");
break;
}
},
}
}
// move on to next
sv_ptr += SVNN_SIZE;
sv_identified = true;
obs_identified = false;
}
// [Observable] identification
// - locate [Observable]s specs for this [SV]
if !obs_identified {
observables = if sv.constellation.is_sbas() {
if let Some(observables) = head_observables.get(&Constellation::SBAS) {
observables
} else {
//#[cfg(feature = "log")]
//error!("parse_sig_v2 (sbas): no specs");
break;
}
} else {
if let Some(observables) = head_observables.get(&sv.constellation) {
observables
} else {
//#[cfg(feature = "log")]
//error!("parse_sig_v2 ({}): no specs", sv.constellation);
break;
}
};
obs_ptr = 0;
obs_identified = true;
//#[cfg(feature = "log")]
//debug!("{}: {:?}", sv, observables);
}
let line_width = line.len();
let trimmed_len = line.trim().len();
if trimmed_len == 0 {
//#[cfg(feature = "log")]
//error!("empty line: \"{}\"", line);
obs_ptr += MAX_OBSERVABLES_LINE;
// this concludes this vehicle
if obs_ptr >= observables.len() {
sv_identified = false;
obs_identified = false;
if sv_ptr == systems_str_len {
// we're done
return;
}
}
continue;
}
// num obs contained this line
let num_obs = div_ceil(line_width, OBSERVABLE_WIDTH);
let mut offset = 0;
//#[cfg(feature = "log")]
//debug!(
// "line: \"{}\" [sv={}/{} obs={}/{}]",
// line,
// sv_ptr,
// systems_str_len,
// obs_ptr,
// observables.len()
//);
// process all of them
for _ in 0..num_obs {
if obs_ptr >= observables.len() {
// line is abnormally long (trailing whitespaces): abort
return;
}
let end = (offset + OBSERVABLE_WIDTH).min(line_width);
let slice = &line[offset..end];
//#[cfg(feature = "log")]
//println!("observation: [{}] \"{}\" {}", sv, slice, observables[obs_ptr]);
// parse possible LLI
let mut lli = Option::<LliFlags>::None;
if slice.len() > OBSERVABLE_F14_WIDTH {
let lli_slice = &slice[OBSERVABLE_F14_WIDTH..OBSERVABLE_F14_WIDTH + 1];
//println!("lli: \"{}\"", lli_slice);
match lli_slice.parse::<u8>() {
Ok(unsigned) => {
lli = LliFlags::from_bits(unsigned);
},
Err(_) => {},
}
}
// parse possible SNR
let mut snr = Option::<SNR>::None;
if slice.len() > OBSERVABLE_F14_WIDTH + 1 {
let snr_slice = &slice[OBSERVABLE_F14_WIDTH + 1..OBSERVABLE_F14_WIDTH + 2];
match SNR::from_str(snr_slice) {
Ok(found) => {
//println!("SNR (OK): \"{}\" [{:?}]", snr_slice, observables[obs_ptr]); // DEBUG
snr = Some(found);
},
Err(_) => {
// println!("SNR (ERR): {:?} [{:?}]", e, observables[obs_ptr]); // DEBUG
},
}
}
// parse observed value
let end = slice.len().min(OBSERVABLE_F14_WIDTH);
if let Ok(value) = parse_f64(slice[..end].trim()) {
signals.push(SignalObservation {
sv,
snr,
lli,
value,
observable: observables[obs_ptr].clone(),
});
}
obs_ptr += 1;
offset += OBSERVABLE_F14_WIDTH + 2;
if obs_ptr == observables.len() {
//#[cfg(feature = "log")]
//debug!("{} completed", sv);
sv_identified = false;
obs_identified = false;
if sv_ptr == systems_str_len {
// we're done
return;
}
} else {
//#[cfg(feature = "log")]
//debug!("{}/{}", obs_ptr, observables.len());
}
} //num_obs
if num_obs < MAX_OBSERVABLES_LINE {
obs_ptr += MAX_OBSERVABLES_LINE - num_obs;
}
} // for all lines provided
}
/// Parses all [SignalObservation]s described by following [Lines].
fn parse_signals_v3(
head_observables: &HashMap<Constellation, Vec<Observable>>,
lines: Lines<'_>,
signals: &mut Vec<SignalObservation>,
) {
const SVNN_SIZE: usize = 3;
const OBSERVABLE_F14_WIDTH: usize = 14;
const OBSERVABLE_WIDTH: usize = 16; // F14 +2 flags
let mut sv; // single alloc
// browse all lines
for line in lines {
// identify SV
let sv_str = &line[0..SVNN_SIZE];
match SV::from_str(sv_str) {
Ok(found) => {
sv = found;
},
Err(_) => {
continue;
},
}
// identify [Observable]s
let observables = if sv.constellation.is_sbas() {
head_observables.get(&Constellation::SBAS)
} else {
head_observables.get(&sv.constellation)
};
if observables.is_none() {
continue;
}
let observables = observables.unwrap();
let num_obs = line.len() / OBSERVABLE_WIDTH;
let mut offset = SVNN_SIZE + 1;
for i in 0..num_obs {
if i == observables.len() {
// line is abnormally long (trailing whitespaces)
break;
}
let end = (offset + OBSERVABLE_WIDTH).min(line.len());
let slice = &line[offset..end];
let mut lli = Option::<LliFlags>::None;
if slice.len() > OBSERVABLE_F14_WIDTH {
let start = offset + OBSERVABLE_F14_WIDTH - 1;
let lli_slice = &line[start..start + 1];
match lli_slice.parse::<u8>() {
Ok(unsigned) => {
lli = LliFlags::from_bits(unsigned);
},
Err(_e) => {
//#[cfg(feature = "log")]
//error!("lli: {}", e);
},
}
}
let mut snr = Option::<SNR>::None;
if slice.len() > OBSERVABLE_F14_WIDTH + 1 {
let start = offset + OBSERVABLE_F14_WIDTH;
let snr_slice = &line[start..start + 1];
if let Ok(value) = snr_slice.parse::<u8>() {
snr = Some(SNR::from(value));
}
}
let end = OBSERVABLE_F14_WIDTH.min(slice.len());
if let Ok(value) = parse_f64(slice[..end].trim()) {
signals.push(SignalObservation {
sv,
value,
lli,
snr,
observable: observables[i].clone(),
});
}
offset += OBSERVABLE_F14_WIDTH + 2;
}
} //browse all lines
}
#[cfg(test)]
mod test {
use super::is_new_epoch;
use crate::{
observation::{EpochFlag, SignalObservation, SNR},
prelude::{Constellation, Observable, Version, SV},
tests::toolkit::generic_observation_epoch_decoding_test,
};
use std::str::FromStr;
#[test]
fn test_new_epoch() {
assert!(is_new_epoch(
"95 01 01 00 00 00.0000000 0 7 06 17 21 22 23 28 31",
Version { major: 2, minor: 0 }
));
assert!(!is_new_epoch(" ", Version { major: 2, minor: 0 }));
assert!(!is_new_epoch(" ", Version { major: 2, minor: 0 }));
assert!(!is_new_epoch("", Version { major: 2, minor: 0 }));
assert!(!is_new_epoch(
"21700656.31447 16909599.97044 .00041 24479973.67844 24479975.23247",
Version { major: 2, minor: 0 }
));
assert!(!is_new_epoch(
" 20849594.124 111570400.06907 86776964.96746 20849589.804 49.000",
Version { major: 2, minor: 0 }
));
assert!(!is_new_epoch(
" 21911712.315 7 21911713.545 7 21911710.943 8 115146830.08007 89724806.65908",
Version { major: 2, minor: 0 },
));
assert!(is_new_epoch(
"95 01 01 11 00 00.0000000 0 8 04 16 18 19 22 24 27 29",
Version { major: 2, minor: 0 }
));
assert!(!is_new_epoch(
"95 01 01 11 00 00.0000000 0 8 04 16 18 19 22 24 27 29",
Version { major: 3, minor: 0 }
));
assert!(is_new_epoch(
"> 2022 01 09 00 00 30.0000000 0 40",
Version { major: 3, minor: 0 }
));
assert!(!is_new_epoch(
"> 2022 01 09 00 00 30.0000000 0 40",
Version { major: 2, minor: 0 }
));
assert!(!is_new_epoch(
"G01 22331467.880 117352685.28208 48.950 22331469.28",
Version { major: 3, minor: 0 }
));
}
#[test]
fn test_parse_v3_1() {
let content =
"> 2022 03 04 0 0 0.0000000 0 22 .000000000000
G01 20832393.682 109474991.854 8 49.500 20832389.822 85305196.437 8 49.500
G03 20342516.786 106900663.487 8 50.000 20342512.006 83299201.382 8 50.000
G04 22448754.952 117969025.322 8 48.250 22448749.312 91923884.833 7 43.750
G06 24827263.216 130468159.526 6 39.750 24827259.316 101663482.505 6 37.250
G09 25493930.890 133971510.403 6 41.250 25493926.950 104393373.997 6 41.750
";
generic_observation_epoch_decoding_test(
content,
3,
Constellation::GPS,
&[
("GPS", "C1C, L1C, S1C, C2P, C2W, C2S, C2L, C2X, L2P, L2W, L2S, L2L, L2X, S2P, S2W, S2S, S2L, S2X"),
],
"2022-03-04T00:00:00 GPST",
30,
"2022-03-04T00:00:00 GPST",
EpochFlag::Ok,
None,
vec![
SignalObservation {
sv: SV::from_str("G01").unwrap(),
observable: Observable::from_str("C1C").unwrap(),
value: 20832393.682,
lli: None,
snr: None,
},
SignalObservation {
sv: SV::from_str("G01").unwrap(),
observable: Observable::from_str("L1C").unwrap(),
value: 109474991.854,
lli: None,
snr: Some(SNR::from(8)),
},
SignalObservation {
sv: SV::from_str("G01").unwrap(),
observable: Observable::from_str("S1C").unwrap(),
value: 49.5,
lli: None,
snr: None,
},
SignalObservation {
sv: SV::from_str("G09").unwrap(),
observable: Observable::from_str("L1C").unwrap(),
value: 133971510.403,
lli: None,
snr: Some(SNR::from(6)),
},
],
);
}
#[test]
fn test_parse_v3_2() {
let content =
"> 2022 03 04 00 00 0.0000000 0 9
G01 20176608.780 106028802.11808 -1009.418 50.250 20176610.080 82619851.24609 -786.562 54.500
G03 20719565.760 108882069.81508 762.203 49.750 20719566.420 84843175.68809 593.922 55.000
G04 21342618.100 112156219.39808 2167.688 48.250 21342617.440 87394474.09607 1689.105 45.500 104393373.997 6 41.750
";
generic_observation_epoch_decoding_test(
content,
3,
Constellation::GPS,
&[("GPS", "C1C, L1C, D1C, S1C, C2W, L2W, D2W, S2W")],
"2022-03-04T00:00:00 GPST",
24,
"2022-03-04T00:00:00 GPST",
EpochFlag::Ok,
None,
vec![],
);
}
#[test]
fn test_parse_v2_1() {
let content = " 21 01 01 00 00 00.0000000 0 24G07G08G10G13G15G16G18G20G21G23G26G27
G30R01R02R03R08R09R15R16R17R18R19R24
24178026.635 6 24178024.891 6 127056391.69906 99004963.01703
24178026.139 3 24178024.181 3 38.066 22.286
21866748.928 7 21866750.407 7 21866747.537 8 114910552.08207 89540700.32608
85809828.27608 21866748.200 8 21866749.482 8 45.759 49.525
52.161
21458907.960 8 21458908.454 7 21458905.489 8 112767333.29708 87870655.27209
84209365.43808 21458907.312 9 21458908.425 9 50.526 55.388
53.157
25107711.730 5 131941919.38305 102811868.09001
25107711.069 1 25107709.586 1 33.150 8.952
24224693.760 6 24224693.174 5 127301651.00206 99196079.53805
24224693.407 5 24224691.898 5 36.121 31.645
21749627.212 8 114295057.63608 89061063.16706
21749626.220 6 21749624.795 6 48.078 39.240
23203962.113 6 23203960.554 6 23203963.222 7 121937655.11806 95016353.74904
91057352.20207 23203961.787 4 23203960.356 4 41.337 28.313
";
generic_observation_epoch_decoding_test(
content,
2,
Constellation::GPS,
&[("GPS", "C1, C2, C5, L1, L2, L5, P1, P2, S1, S2, S5")],
"2021-01-01T00:00:00 GPST",
62,
"2021-01-01T00:00:00 GPST",
EpochFlag::Ok,
None,
vec![],
);
}
#[test]
fn test_parse_v2_2() {
// extracted from v2/npaz3550_21o
let content = " 21 12 21 00 00 30.0000000 0 17G08G10G15G16G18G21G23G26G32R04R05R06
R10R12R19R20R21
22273618.192 117048642.67706 91206745.37646 22273620.492 45.000
27.000
20679832.284 108673270.42707 84680474.73748 20679834.464 51.000
47.000
24428574.718 128373032.12605 100030940.88146 24428574.158 40.000
26.000
21748622.436 114289784.86106 89056973.16346 21748622.736 46.000
30.000
23159990.646 121706608.75906 94836327.00246 23159990.946 43.000
22.000
24121222.480 126757874.84305 98772376.29745 24121222.660 40.000
17.000
21243754.540 111636702.47207 86989641.56447 21243755.220 48.000
33.000
23730924.382 124706843.12805 97174162.95546 23730927.462 41.000
30.000
25068865.594 131737775.26304 102652811.50846 25068867.574 38.000
24.000
21628749.716 115820985.39407 90082976.65346 21628746.496 48.000
22.000
20263702.068 108321110.70607 84249758.00647 20263698.768 50.000
34.000
23144036.268 123501011.94300 32.000
22905715.968 122100341.64505 42.000
23665462.924 126416648.11306 98324062.16346 23665459.604 43.000
25.000
23266753.448 124461433.23101 33.000
19895900.968 106392328.16604 82749588.85847 19895899.328 39.000
33.000
21758080.616 116431896.04406 90558129.99946 21758077.536 47.000
22.000 ";
generic_observation_epoch_decoding_test(
content,
2,
Constellation::Mixed,
&[
("GPS", "C1, L1, L2, P2, S1, S2"),
("GLO", "C1, L1, L2, P2, S1, S2"),
],
"2021-12-21T00:00:30 GPST",
93,
"2021-12-21T00:00:30 GPST",
EpochFlag::Ok,
None,
vec![],
);
}
#[test]
fn test_parse_v2_barq() {
// extracted from v2/barq0.19d
let content = " 19 3 12 16 36 0.0000000 0 15G08G10G14G16G18G20G22G26G27G32R04R05
R06R19R20
111525030.92718 86902614.11057 21222508.060 21222505.880
113917475.72718 88766858.51957 21677775.000 21677773.380
117582228.43417 91622504.36956 22375154.360 22375150.800
116496701.38017 90776637.01356 22168585.640 22168581.380
120543799.60817 93930215.60156 22938722.560 22938718.700
125411881.34917 97723533.46955 23865086.320 23865082.480
124148906.10717 96739381.30556 23624751.240 23624745.820
127085060.78216 99027334.17655 24183483.520 24183486.540
105970568.06418 82574457.73858 20165528.740 20165526.020
114286506.03418 89054414.91257 21747998.820 21747997.860
113282224.20717 88108400.20417 21154656.900 21154656.960
102507243.91718 79727864.12617 19176099.600 19176101.880
116775184.83016 21883618.780
112181738.38117 87252468.04516 20971192.900 20971194.040
110923986.30317 86274222.72417 20743344.820 20743348.200
";
generic_observation_epoch_decoding_test(
content,
2,
Constellation::Mixed,
&[
("GPS", "L1, L2, C1, P1, P2, P1, P2"),
("GLO", "L1, L2, C1, P1, P2, P1, P2"),
],
"2019-03-12T16:36:00 GPST",
50,
"2019-03-12T16:36:00 GPST",
EpochFlag::Ok,
None,
vec![],
);
}
#[test]
fn test_parse_v2_20th_century() {
// extracted from v2/barq0.19d
let content = " 95 3 12 16 36 0.0000000 0 15G08G10G14G16G18G20G22G26G27G32R04R05
R06R19R20
111525030.92718 86902614.11057 21222508.060 21222505.880
113917475.72718 88766858.51957 21677775.000 21677773.380
117582228.43417 91622504.36956 22375154.360 22375150.800
116496701.38017 90776637.01356 22168585.640 22168581.380
120543799.60817 93930215.60156 22938722.560 22938718.700
125411881.34917 97723533.46955 23865086.320 23865082.480
124148906.10717 96739381.30556 23624751.240 23624745.820
127085060.78216 99027334.17655 24183483.520 24183486.540
105970568.06418 82574457.73858 20165528.740 20165526.020
114286506.03418 89054414.91257 21747998.820 21747997.860
113282224.20717 88108400.20417 21154656.900 21154656.960
102507243.91718 79727864.12617 19176099.600 19176101.880
116775184.83016 21883618.780
112181738.38117 87252468.04516 20971192.900 20971194.040
110923986.30317 86274222.72417 20743344.820 20743348.200
";
generic_observation_epoch_decoding_test(
content,
2,
Constellation::Mixed,
&[
("GPS", "L1, L2, C1, P1, P2, P1, P2"),
("GLO", "L1, L2, C1, P1, P2, P1, P2"),
],
"1995-03-12T16:36:00 GPST",
50,
"1995-03-12T16:36:00 GPST",
EpochFlag::Ok,
None,
vec![],
);
}
}