Skip to content

Commit 492a7e5

Browse files
authored
Create Skytracker and improved API (#40)
* Create Skytracker and improved API * Add more tests --------- Signed-off-by: Guillaume W. Bres <[email protected]>
1 parent fe21dac commit 492a7e5

File tree

4 files changed

+266
-19
lines changed

4 files changed

+266
-19
lines changed

src/scheduler/calendar.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,6 @@ impl CommonViewCalendar {
182182

183183
let mjd_midnight = Epoch::from_mjd_utc(mjd as f64);
184184
let mjd_next_midnight = Epoch::from_mjd_utc((mjd + 1) as f64);
185-
let time_to_midnight = mjd_next_midnight - mjd_midnight;
186185

187186
// offset of first period of that day
188187
let offset_nanos = self.first_start_offset_nanos(mjd) as f64;

src/tracker/fit.rs

Lines changed: 228 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@ use log::debug;
44
use polyfit_rs::polyfit_rs::polyfit;
55
use thiserror::Error;
66

7-
use crate::prelude::{Duration, Epoch, FittedData, IonosphericData, TrackData, SV};
8-
9-
use std::collections::BTreeMap;
7+
use crate::prelude::{Duration, Epoch, FittedData, SV};
108

119
/// CGGTTS track formation errors
1210
#[derive(Debug, Clone, Error)]
@@ -59,22 +57,28 @@ pub struct Observation {
5957
}
6058

6159
impl SVTracker {
62-
/// Creates (allocates) a new [SVTracker] for that particular satellite.
60+
/// Allocate a new [SVTracker] for that particular satellite.
6361
///
6462
/// ## Input
65-
/// - satellite: as [SV]
66-
/// - tolerance: sampling gap tolerance as [Duration]
67-
pub fn new(sv: SV, gap_tolerance: Option<Duration>) -> Self {
63+
/// - satellite: [SV]
64+
pub fn new(satellite: SV) -> Self {
6865
Self {
69-
sv,
7066
size: 0,
7167
t0: None,
7268
prev_t: None,
73-
gap_tolerance,
69+
sv: satellite,
70+
gap_tolerance: None,
7471
buffer: Vec::with_capacity(16),
7572
}
7673
}
7774

75+
/// Define a new [SVTracker] with desired observation gap tolerance.
76+
pub fn with_gap_tolerance(&self, tolerance: Duration) -> Self {
77+
let mut s = self.clone();
78+
s.gap_tolerance = Some(tolerance);
79+
s
80+
}
81+
7882
/// Feed new [Observation] at t [Epoch] of observation (sampling).
7983
/// Although CGGTTS works in UTC internally, we accept any timescale here.
8084
/// Samples must be provided in chronological order.
@@ -288,14 +292,13 @@ impl SVTracker {
288292

289293
#[cfg(test)]
290294
mod test {
291-
use crate::prelude::{Duration, Epoch, FittedData, Observation, SVTracker, SV};
295+
use crate::prelude::{Duration, Epoch, Observation, SVTracker, SV};
292296
use std::str::FromStr;
293297

294298
#[test]
295299
fn tracker_no_gap_x3() {
296300
let g01 = SV::from_str("G01").unwrap();
297-
298-
let mut tracker = SVTracker::new(g01, None);
301+
let mut tracker = SVTracker::new(g01);
299302

300303
for obs in [
301304
Observation {
@@ -357,8 +360,7 @@ mod test {
357360
#[test]
358361
fn tracker_no_gap_x4() {
359362
let g01 = SV::from_str("G01").unwrap();
360-
361-
let mut tracker = SVTracker::new(g01, None);
363+
let mut tracker = SVTracker::new(g01);
362364

363365
for obs in [
364366
Observation {
@@ -416,4 +418,216 @@ mod test {
416418
assert_eq!(fitted.elevation_deg, 6.1);
417419
assert_eq!(fitted.azimuth_deg, 7.1);
418420
}
421+
422+
#[test]
423+
fn tracker_30s_15s_ok() {
424+
let g01 = SV::from_str("G01").unwrap();
425+
let dt_30s = Duration::from_str("30 s").unwrap();
426+
427+
let mut tracker = SVTracker::new(g01).with_gap_tolerance(dt_30s);
428+
429+
for obs in [
430+
Observation {
431+
epoch: Epoch::from_str("2020-01-01T00:00:00 UTC").unwrap(),
432+
refsv: 1.0,
433+
refsys: 2.0,
434+
mdtr: 3.0,
435+
mdio: 4.0,
436+
msio: None,
437+
elevation: 6.0,
438+
azimuth: 7.0,
439+
},
440+
Observation {
441+
epoch: Epoch::from_str("2020-01-01T00:00:15 UTC").unwrap(),
442+
refsv: 1.1,
443+
refsys: 2.1,
444+
mdtr: 3.1,
445+
mdio: 4.1,
446+
msio: None,
447+
elevation: 6.1,
448+
azimuth: 7.1,
449+
},
450+
Observation {
451+
epoch: Epoch::from_str("2020-01-01T00:00:30 UTC").unwrap(),
452+
refsv: 1.1,
453+
refsys: 2.1,
454+
mdtr: 3.1,
455+
mdio: 4.1,
456+
msio: None,
457+
elevation: 6.1,
458+
azimuth: 7.1,
459+
},
460+
Observation {
461+
epoch: Epoch::from_str("2020-01-01T00:00:45 UTC").unwrap(),
462+
refsv: 1.1,
463+
refsys: 2.1,
464+
mdtr: 3.1,
465+
mdio: 4.1,
466+
msio: None,
467+
elevation: 6.1,
468+
azimuth: 7.1,
469+
},
470+
] {
471+
tracker.new_observation(obs);
472+
}
473+
474+
assert!(tracker.fit().is_ok());
475+
}
476+
477+
#[test]
478+
fn tracker_30s_30s_ok() {
479+
let g01 = SV::from_str("G01").unwrap();
480+
let dt_30s = Duration::from_str("30 s").unwrap();
481+
482+
let mut tracker = SVTracker::new(g01).with_gap_tolerance(dt_30s);
483+
484+
for obs in [
485+
Observation {
486+
epoch: Epoch::from_str("2020-01-01T00:00:00 UTC").unwrap(),
487+
refsv: 1.0,
488+
refsys: 2.0,
489+
mdtr: 3.0,
490+
mdio: 4.0,
491+
msio: None,
492+
elevation: 6.0,
493+
azimuth: 7.0,
494+
},
495+
Observation {
496+
epoch: Epoch::from_str("2020-01-01T00:00:30 UTC").unwrap(),
497+
refsv: 1.1,
498+
refsys: 2.1,
499+
mdtr: 3.1,
500+
mdio: 4.1,
501+
msio: None,
502+
elevation: 6.1,
503+
azimuth: 7.1,
504+
},
505+
Observation {
506+
epoch: Epoch::from_str("2020-01-01T00:01:00 UTC").unwrap(),
507+
refsv: 1.1,
508+
refsys: 2.1,
509+
mdtr: 3.1,
510+
mdio: 4.1,
511+
msio: None,
512+
elevation: 6.1,
513+
azimuth: 7.1,
514+
},
515+
Observation {
516+
epoch: Epoch::from_str("2020-01-01T00:01:30 UTC").unwrap(),
517+
refsv: 1.1,
518+
refsys: 2.1,
519+
mdtr: 3.1,
520+
mdio: 4.1,
521+
msio: None,
522+
elevation: 6.1,
523+
azimuth: 7.1,
524+
},
525+
] {
526+
tracker.new_observation(obs);
527+
}
528+
529+
assert!(tracker.fit().is_ok());
530+
531+
for obs in [
532+
Observation {
533+
epoch: Epoch::from_str("2020-01-01T00:00:00 UTC").unwrap(),
534+
refsv: 1.0,
535+
refsys: 2.0,
536+
mdtr: 3.0,
537+
mdio: 4.0,
538+
msio: None,
539+
elevation: 6.0,
540+
azimuth: 7.0,
541+
},
542+
Observation {
543+
epoch: Epoch::from_str("2020-01-01T00:00:30 UTC").unwrap(),
544+
refsv: 1.1,
545+
refsys: 2.1,
546+
mdtr: 3.1,
547+
mdio: 4.1,
548+
msio: None,
549+
elevation: 6.1,
550+
azimuth: 7.1,
551+
},
552+
Observation {
553+
epoch: Epoch::from_str("2020-01-01T00:01:00 UTC").unwrap(),
554+
refsv: 1.1,
555+
refsys: 2.1,
556+
mdtr: 3.1,
557+
mdio: 4.1,
558+
msio: None,
559+
elevation: 6.1,
560+
azimuth: 7.1,
561+
},
562+
Observation {
563+
epoch: Epoch::from_str("2020-01-01T00:01:15 UTC").unwrap(),
564+
refsv: 1.1,
565+
refsys: 2.1,
566+
mdtr: 3.1,
567+
mdio: 4.1,
568+
msio: None,
569+
elevation: 6.1,
570+
azimuth: 7.1,
571+
},
572+
] {
573+
tracker.new_observation(obs);
574+
}
575+
576+
assert!(tracker.fit().is_ok());
577+
}
578+
579+
#[test]
580+
fn tracker_30s_nok() {
581+
let g01 = SV::from_str("G01").unwrap();
582+
let dt_30s = Duration::from_str("30 s").unwrap();
583+
584+
let mut tracker = SVTracker::new(g01).with_gap_tolerance(dt_30s);
585+
586+
for obs in [
587+
Observation {
588+
epoch: Epoch::from_str("2020-01-01T00:00:00 UTC").unwrap(),
589+
refsv: 1.0,
590+
refsys: 2.0,
591+
mdtr: 3.0,
592+
mdio: 4.0,
593+
msio: None,
594+
elevation: 6.0,
595+
azimuth: 7.0,
596+
},
597+
Observation {
598+
epoch: Epoch::from_str("2020-01-01T00:00:30 UTC").unwrap(),
599+
refsv: 1.1,
600+
refsys: 2.1,
601+
mdtr: 3.1,
602+
mdio: 4.1,
603+
msio: None,
604+
elevation: 6.1,
605+
azimuth: 7.1,
606+
},
607+
Observation {
608+
epoch: Epoch::from_str("2020-01-01T00:01:00 UTC").unwrap(),
609+
refsv: 1.1,
610+
refsys: 2.1,
611+
mdtr: 3.1,
612+
mdio: 4.1,
613+
msio: None,
614+
elevation: 6.1,
615+
azimuth: 7.1,
616+
},
617+
Observation {
618+
epoch: Epoch::from_str("2020-01-01T00:01:31 UTC").unwrap(),
619+
refsv: 1.1,
620+
refsys: 2.1,
621+
mdtr: 3.1,
622+
mdio: 4.1,
623+
msio: None,
624+
elevation: 6.1,
625+
azimuth: 7.1,
626+
},
627+
] {
628+
tracker.new_observation(obs);
629+
}
630+
631+
assert!(tracker.fit().is_err());
632+
}
419633
}

src/tracker/fitted.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ impl FittedData {
5151
/// For Glonass, it should be between 1-96 as the date of ephemeris as
5252
/// daily quarters of hours, starting at 1 for 00:00:00 midnight.
5353
/// For BeiDou, the hour of clock, between 0-23 should be used.
54-
pub fn to_track(&self, class: CommonViewClass, data: u16) -> Track {
54+
/// - rinex_code: RINEX code.
55+
pub fn to_track(&self, class: CommonViewClass, data: u16, rinex_code: &str) -> Track {
5556
Track {
5657
class,
5758
epoch: self.first_t,
@@ -83,7 +84,7 @@ impl FittedData {
8384
},
8485
// TODO
8586
hc: 0,
86-
frc: "C1C".to_string(), // TODO
87+
frc: rinex_code.to_string(),
8788
}
8889
}
8990
}

src/tracker/mod.rs

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,46 @@ mod fitted;
44
pub use fit::{FitError, Observation, SVTracker};
55
pub use fitted::FittedData;
66

7-
use crate::prelude::SV;
7+
use crate::prelude::{Duration, SV};
88
use std::collections::HashMap;
99

1010
/// [SkyTracker] is used to track all Satellite vehicles
1111
/// in sight during a common view period and eventually collect CGGTTS.
1212
#[derive(Default, Debug, Clone)]
1313
pub struct SkyTracker {
1414
/// Internal buffer
15-
sv_trackers: HashMap<SV, SVTracker>,
15+
trackers: HashMap<SV, SVTracker>,
16+
/// Gap tolerance
17+
gap_tolerance: Option<Duration>,
18+
}
19+
20+
impl SkyTracker {
21+
/// Allocate new [SkyTracker]
22+
pub fn new() -> Self {
23+
Self {
24+
trackers: HashMap::with_capacity(8),
25+
gap_tolerance: None,
26+
}
27+
}
28+
29+
/// Define a [SkyTracker] with desired observation gap tolerance.
30+
pub fn with_gap_tolerance(&self, tolerance: Duration) -> Self {
31+
let mut s = self.clone();
32+
s.gap_tolerance = Some(tolerance);
33+
s
34+
}
35+
36+
/// Provide new [Observation] for that particular satellite.
37+
pub fn new_observation(&mut self, satellite: SV, data: Observation) {
38+
if let Some(tracker) = self.trackers.get_mut(&satellite) {
39+
tracker.new_observation(data);
40+
} else {
41+
let mut new = SVTracker::new(satellite);
42+
if let Some(tolerance) = self.gap_tolerance {
43+
new = new.with_gap_tolerance(tolerance);
44+
}
45+
new.new_observation(data);
46+
self.trackers.insert(satellite, new);
47+
}
48+
}
1649
}

0 commit comments

Comments
 (0)