sbpdump/
lib.rs

1extern crate serde_json;
2
3use serde_json::Value;
4use std::collections::{BTreeMap, BTreeSet, HashSet};
5use std::fmt;
6use std::fs::File;
7use std::io::{BufRead, BufReader, Error};
8
9/// Observation message number.
10const MSG_OBS: u64 = 74;
11
12/// GPS ephemeris message number.
13const MSG_EPHEMERIS_GPS: u64 = 138;
14
15/// Galileo ephemeris message number.
16const MSG_EPHEMERIS_GAL: u64 = 149;
17
18/// SSR combined orbit and clock message number.
19const MSG_SSR_ORBIT_CLOCK: u64 = 1501;
20
21/// SSR code bias message number.
22const MSG_SSR_CODE_BIASES: u64 = 1505;
23
24/// SSR phase bias message number.
25const MSG_SSR_PHASE_BIASES: u64 = 1510;
26
27/// GPS signal codes.
28const GPS_CODES: [u64; 12] = [0, 1, 5, 6, 7, 8, 9, 10, 11, 56, 57, 58];
29
30/// Galileo signal codes.
31const GAL_CODES: [u64; 16] = [
32    14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 61,
33];
34
35/// Signal identifier with optional issue of data.
36#[derive(Ord, PartialOrd, Eq, PartialEq, Clone, Debug)]
37struct Sid {
38    sat: u64,
39    code: u64,
40    iod: Option<u64>,
41}
42
43impl fmt::Display for Sid {
44    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
45        if let Some(iod) = self.iod {
46            write!(fmt, "{}:{}-{}", self.sat, self.code, iod)
47        } else {
48            write!(fmt, "{}:{}", self.sat, self.code)
49        }
50    }
51}
52
53/// Message information to dump.
54#[derive(Debug)]
55struct Msg {
56    msg_type: u64,
57    sender: u64,
58    tow: u64,
59    sid_set: BTreeSet<Sid>,
60}
61
62impl Msg {
63    /// Build message from all GPS and Galileo observations.
64    fn observations(value: &Value, msg_type: u64, sender: u64) -> Option<Msg> {
65        value["header"]["t"]["tow"].as_u64().and_then(|tow| {
66            value["obs"].as_array().and_then(|obs| {
67                let mut sid_set: BTreeSet<Sid> = BTreeSet::new();
68                for ob in obs.iter() {
69                    if let Some(sat) = ob["sid"]["sat"].as_u64() {
70                        if let Some(code) = ob["sid"]["code"].as_u64() {
71                            sid_set.insert(Sid {
72                                sat,
73                                code,
74                                iod: None,
75                            });
76                        }
77                    }
78                }
79                Some(Msg {
80                    msg_type,
81                    sender,
82                    tow: tow / 1000,
83                    sid_set,
84                })
85            })
86        })
87    }
88
89    /// Build message from GPS and Galileo ephemerides.
90    fn ephemerides(value: &Value, msg_type: u64, sender: u64) -> Option<Msg> {
91        value["common"]["toe"]["tow"].as_u64().and_then(|tow| {
92            value["common"]["sid"]["sat"].as_u64().and_then(|sat| {
93                value["common"]["sid"]["code"].as_u64().and_then(|code| {
94                    let mut sid_set: BTreeSet<Sid> = BTreeSet::new();
95                    sid_set.insert(Sid {
96                        sat,
97                        code,
98                        iod: value["iode"].as_u64(),
99                    });
100                    Some(Msg {
101                        msg_type,
102                        sender,
103                        tow,
104                        sid_set,
105                    })
106                })
107            })
108        })
109    }
110
111    /// Build message from GPS and Galileo SSR corrections.
112    fn corrections(value: &Value, msg_type: u64, sender: u64) -> Option<Msg> {
113        value["time"]["tow"].as_u64().and_then(|tow| {
114            value["sid"]["sat"].as_u64().and_then(|sat| {
115                value["sid"]["code"].as_u64().and_then(|code| {
116                    let mut sid_set: BTreeSet<Sid> = BTreeSet::new();
117                    sid_set.insert(Sid {
118                        sat,
119                        code,
120                        iod: value["iod"].as_u64(),
121                    });
122                    Some(Msg {
123                        msg_type,
124                        sender,
125                        tow,
126                        sid_set,
127                    })
128                })
129            })
130        })
131    }
132
133    /// Build message.
134    fn new(value: &Value) -> Option<Msg> {
135        value["msg_type"].as_u64().and_then(|msg_type| {
136            value["sender"].as_u64().and_then(|sender| match msg_type {
137                MSG_OBS => Self::observations(value, msg_type, sender),
138                MSG_EPHEMERIS_GPS => Self::ephemerides(value, msg_type, sender),
139                MSG_EPHEMERIS_GAL => Self::ephemerides(value, msg_type, sender),
140                MSG_SSR_ORBIT_CLOCK => Self::corrections(value, msg_type, sender),
141                MSG_SSR_CODE_BIASES => Self::corrections(value, msg_type, sender),
142                MSG_SSR_PHASE_BIASES => Self::corrections(value, msg_type, sender),
143                _ => None,
144            })
145        })
146    }
147}
148
149/// Dump messages from file.
150pub fn dump(file: &File, matched: bool, gps: bool, gal: bool) -> Result<(), Error> {
151    let buf = BufReader::new(file);
152
153    let mut code_set: HashSet<u64> = HashSet::new();
154    if gps {
155        for code in GPS_CODES.iter() {
156            code_set.insert(*code);
157        }
158    }
159    if gal {
160        for code in GAL_CODES.iter() {
161            code_set.insert(*code);
162        }
163    }
164
165    if matched {
166        let mut tow_map: BTreeMap<u64, BTreeMap<u64, BTreeMap<u64, BTreeSet<Sid>>>> =
167            BTreeMap::new();
168        for line in buf.lines() {
169            let value: Value = serde_json::from_str(&(line?))?;
170            if let Some(msg) = Msg::new(&value) {
171                let mut sid_set = msg.sid_set;
172                tow_map
173                    .entry(msg.tow)
174                    .or_insert_with(BTreeMap::new)
175                    .entry(msg.sender)
176                    .or_insert_with(BTreeMap::new)
177                    .entry(msg.msg_type)
178                    .or_insert_with(BTreeSet::new)
179                    .append(&mut sid_set);
180            }
181        }
182
183        for (tow, sender_map) in tow_map.iter() {
184            for (sender, msg_type_map) in sender_map.iter() {
185                for (msg_type, sid_set) in msg_type_map.iter() {
186                    let mut sid_set_str = String::new();
187                    for sid in sid_set.iter() {
188                        if code_set.contains(&sid.code) {
189                            sid_set_str.push_str(&format!("{} ", sid));
190                        }
191                    }
192                    println!("{:>6} {:>5} {:>4} {}", tow, sender, msg_type, sid_set_str);
193                }
194            }
195            println!();
196        }
197    } else {
198        for line in buf.lines() {
199            let value: Value = serde_json::from_str(&(line?))?;
200            if let Some(msg) = Msg::new(&value) {
201                let mut sid_set_str = String::new();
202                for sid in msg.sid_set.iter() {
203                    if code_set.contains(&sid.code) {
204                        sid_set_str.push_str(&format!("{} ", sid));
205                    }
206                }
207                println!(
208                    "{:>6} {:>5} {:>4} {}",
209                    msg.tow, msg.sender, msg.msg_type, sid_set_str
210                );
211            }
212        }
213    }
214
215    Ok(())
216}