1use unpack;
2
3#[derive(Debug)]
5pub struct ULogData {
6 data: Vec<u8>,
7 formats: Vec<String>,
8}
9
10pub struct ULogDataIter<'a> {
31 data: &'a ULogData,
32 format_index: usize,
33 data_index: usize,
34}
35
36#[derive(Debug, PartialEq)]
38pub enum DataType {
39 UInt64(u64),
40 Int32(i32),
41 Float(f32),
42 UInt8(u8),
43 Bool(bool),
44}
45
46impl ULogData {
47 pub fn new(data: Vec<u8>, formats: Vec<String>) -> Self {
48 Self { data, formats }
49 }
50
51 pub fn data(&self) -> &Vec<u8> {
53 &self.data
54 }
55
56 pub fn formats(&self) -> &Vec<String> {
58 &self.formats
59 }
60
61 pub fn items(&self) -> Vec<String> {
83 self.formats
84 .iter()
85 .filter(|f| f.len() > 0 && !f.contains("_padding") && f.contains(" "))
86 .map(|f| f.split(" ").last().unwrap().to_string())
87 .collect()
88 }
89
90 pub fn iter(&self) -> ULogDataIter {
110 ULogDataIter {
111 data: self,
112 format_index: 0,
113 data_index: 0,
114 }
115 }
116}
117
118impl<'a> Iterator for ULogDataIter<'a> {
119 type Item = (&'a str, DataType);
120
121 fn next(&mut self) -> Option<Self::Item> {
122 if self.format_index > self.data.formats.len() || self.data_index >= self.data.data.len() {
123 None
124 } else {
125 let format = &self.data.formats[self.format_index];
126 self.format_index += 1;
127 let space = format.find(" ").unwrap();
128 let (dtype, fname) = format.split_at(space);
129 let fname = fname.trim();
130
131 match dtype {
132 "uint64_t" => {
133 let data_to = self.data_index + 8;
134 let val = if self.data.data.len() > data_to {
135 let mut buf: [u8; 8] = Default::default();
136 buf.copy_from_slice(&self.data.data[self.data_index..data_to]);
137 self.data_index += 8;
138 unpack::as_u64_le(&buf)
139 } else {
140 0
141 };
142 Some((fname, DataType::UInt64(val)))
143 }
144 "int32_t" => {
145 let data_to = self.data_index + 4;
146 let val = if self.data.data.len() > data_to {
147 let mut buf: [u8; 4] = Default::default();
148 buf.copy_from_slice(&self.data.data[self.data_index..data_to]);
149 self.data_index += 4;
150 unpack::as_i32_le(&buf)
151 } else {
152 0
153 };
154 Some((fname, DataType::Int32(val)))
155 }
156 "float" => {
157 let data_to = self.data_index + 4;
158 let val = if self.data.data.len() > data_to {
159 let mut buf: [u8; 4] = Default::default();
160 buf.copy_from_slice(&self.data.data[self.data_index..data_to]);
161 self.data_index += 4;
162 unpack::as_f32_le(&buf)
163 } else {
164 0.0
165 };
166 Some((fname, DataType::Float(val)))
167 }
168 "uint8_t" => {
169 let val = if self.data.data.len() > self.data_index {
170 let v = self.data.data[self.data_index];
171 self.data_index += 1;
172 v
173 } else {
174 0
175 };
176 Some((fname, DataType::UInt8(val)))
177 }
178 "bool" => {
179 let val = if self.data.data.len() > self.data_index {
180 let v = self.data.data[self.data_index] > 0;
181 self.data_index += 1;
182 v
183 } else {
184 false
185 };
186 Some((fname, DataType::Bool(val)))
187 }
188 _ => None,
189 }
190 }
191 }
192}
193
194#[cfg(test)]
195mod tests {
196 use super::*;
197 use parser::dataset::*;
198 use std::collections::HashMap;
199 use std::fs::File;
200
201 #[test]
202 fn it_parses_the_data() {
203 let filename = format!(
204 "{}/tests/fixtures/6ba1abc7-b433-4029-b8f5-3b2bb12d3b6c.ulg",
205 env!("CARGO_MANIFEST_DIR")
206 );
207
208 let mut log_file = File::open(&filename).unwrap();
209
210 let first_position = log_file
211 .get_dataset("vehicle_gps_position")
212 .unwrap()
213 .next()
214 .unwrap();
215
216 let items = first_position.items();
217 let mut seen = HashMap::new();
218 for item in items.clone() {
219 seen.insert(item.clone(), 0);
220 }
221
222 for (name, data) in first_position.iter() {
223 *seen.get_mut(name).unwrap() += 1;
224 match name {
225 "timestamp" => assert_eq!(DataType::UInt64(375408345), data),
226 "time_utc_usec" => assert_eq!(DataType::UInt64(0), data),
227 "lat" => assert_eq!(DataType::Int32(407423012), data),
228 "lon" => assert_eq!(DataType::Int32(-741792999), data),
229 "alt" => assert_eq!(DataType::Int32(28495), data),
230 "alt_ellipsoid" => assert_eq!(DataType::Int32(0), data),
231 "s_variance_m_s" => assert_eq!(DataType::Float(0.0), data),
232 "c_variance_rad" => assert_eq!(DataType::Float(0.0), data),
233 "eph" => assert_eq!(DataType::Float(0.29999998), data),
234 "epv" => assert_eq!(DataType::Float(0.39999998), data),
235 "hdop" => assert_eq!(DataType::Float(0.0), data),
236 "vdop" => assert_eq!(DataType::Float(0.0), data),
237 "noise_per_ms" => assert_eq!(DataType::Int32(0), data),
238 "jamming_indicator" => assert_eq!(DataType::Int32(0), data),
239 "vel_m_s" => assert_eq!(DataType::Float(0.0), data),
240 "vel_n_m_s" => assert_eq!(DataType::Float(0.0), data),
241 "vel_e_m_s" => assert_eq!(DataType::Float(0.0), data),
242 "vel_d_m_s" => assert_eq!(DataType::Float(0.0), data),
243 "cog_rad" => assert_eq!(DataType::Float(0.0), data),
244 "timestamp_time_relative" => assert_eq!(DataType::Int32(0), data),
245 "fix_type" => assert_eq!(DataType::UInt8(3), data),
246 "vel_ned_valid" => assert_eq!(DataType::Bool(false), data),
247 "satellites_used" => assert_eq!(DataType::UInt8(10), data),
248 x => panic!(format!("unexpected field '{}'", x)),
249 }
250 }
251
252 for item in items {
253 assert_eq!(seen.get(item.as_str()), Some(&1), "item {} not seen", item);
254 }
255 }
256}