use crate::point::Point;
use crate::source::Source;
use crate::units::Radians;
use crate::Error;
use byteorder::{LittleEndian, ReadBytesExt};
use std::fmt::Debug;
use std::fs::File;
use std::io::{BufReader, Read, Seek, SeekFrom};
use std::iter::IntoIterator;
use std::path::Path;
#[derive(Debug)]
pub struct Reader<R: Read + Seek> {
pub avgint: f64,
pub company: [u8; 32],
pub day: u16,
pub device: [u8; 32],
pub devint: f64,
pub entries: i64,
pub location: [u8; 16],
pub maxalt: f64,
pub maxint: f64,
pub maxlat: f64,
pub maxlon: f64,
pub minalt: f64,
pub minlat: f64,
pub minlon: f64,
pub month: u16,
pub project: [u8; 32],
pub timeinfo: TimeInfo,
pub timeunit: TimeUnit,
pub timezone: [u8; 16],
pub version: Version,
pub year: u16,
reader: R,
position: i64,
}
impl Reader<BufReader<File>> {
pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Reader<BufReader<File>>, Error> {
let reader = BufReader::new(File::open(path)?);
Reader::new(reader)
}
}
impl<R: Read + Seek> Reader<R> {
fn new(mut reader: R) -> Result<Reader<R>, Error> {
let mut preamble = [0; 27];
reader.read_exact(&mut preamble)?;
let major = reader.read_u16::<LittleEndian>()?;
let minor = reader.read_u16::<LittleEndian>()?;
let version = Version::new(major, minor);
let data_offset = reader.read_u32::<LittleEndian>()?;
let year = reader.read_u16::<LittleEndian>()?;
let month = reader.read_u16::<LittleEndian>()?;
let day = reader.read_u16::<LittleEndian>()?;
let entries = reader.read_i64::<LittleEndian>()?;
let minlon = reader.read_f64::<LittleEndian>()?;
let maxlon = reader.read_f64::<LittleEndian>()?;
let minlat = reader.read_f64::<LittleEndian>()?;
let maxlat = reader.read_f64::<LittleEndian>()?;
let minalt = reader.read_f64::<LittleEndian>()?;
let maxalt = reader.read_f64::<LittleEndian>()?;
let avgint = reader.read_f64::<LittleEndian>()?;
let maxint = reader.read_f64::<LittleEndian>()?;
let devint = reader.read_f64::<LittleEndian>()?;
let timeunit = TimeUnit::from_u8(reader.read_u8()?)?;
let timeinfo = TimeInfo::from_u8(reader.read_u8()?)?;
let mut timezone = [0; 16];
reader.read_exact(&mut timezone)?;
let mut location = [0; 16];
reader.read_exact(&mut location)?;
let mut device = [0; 32];
reader.read_exact(&mut device)?;
let mut reserved = [0; 32];
reader.read_exact(&mut reserved)?;
let mut project = [0; 32];
reader.read_exact(&mut project)?;
let mut company = [0; 32];
reader.read_exact(&mut company)?;
let mut reserved2 = [0; 32];
reader.read_exact(&mut reserved2)?;
let _ = reader.seek(SeekFrom::Start(data_offset as u64))?;
Ok(Reader {
avgint,
company,
day,
device,
devint,
entries,
location,
maxalt,
maxint,
maxlat,
maxlon,
minalt,
minlat,
minlon,
month,
position: 0,
project,
reader,
timeinfo,
timeunit,
timezone,
version,
year,
})
}
pub fn read_point(&mut self) -> Result<Option<Point>, Error> {
if self.position == self.entries {
return Ok(None);
}
let time = self.reader.read_f64::<LittleEndian>()?;
let longitude = self.reader.read_f64::<LittleEndian>()?;
let latitude = self.reader.read_f64::<LittleEndian>()?;
let altitude = self.reader.read_f64::<LittleEndian>()?;
let roll = self.reader.read_f64::<LittleEndian>()?;
let pitch = self.reader.read_f64::<LittleEndian>()?;
let yaw = self.reader.read_f64::<LittleEndian>()?;
let distance = if self.version.has_distance() {
Some(self.reader.read_f64::<LittleEndian>()?)
} else {
None
};
self.position += 1;
Ok(Some(Point {
time,
longitude: Radians::from_degrees(longitude),
latitude: Radians::from_degrees(latitude),
altitude,
roll: Radians::from_degrees(roll),
pitch: Radians::from_degrees(pitch),
yaw: Radians::from_degrees(yaw),
distance,
..Default::default()
}))
}
}
impl<R: Read + Seek> IntoIterator for Reader<R> {
type Item = Point;
type IntoIter = ReaderIterator<R>;
fn into_iter(self) -> Self::IntoIter {
ReaderIterator { reader: self }
}
}
#[derive(Debug)]
pub struct ReaderIterator<R: Read + Seek> {
reader: Reader<R>,
}
impl<R: Read + Seek> Iterator for ReaderIterator<R> {
type Item = Point;
fn next(&mut self) -> Option<Self::Item> {
self.reader.read_point().unwrap()
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Version {
major: u16,
minor: u16,
}
impl Version {
pub fn new(major: u16, minor: u16) -> Version {
Version { major, minor }
}
fn has_distance(&self) -> bool {
self.minor >= 1
}
}
#[derive(Clone, Copy, Debug)]
pub enum TimeUnit {
Normalized,
Day,
Week,
}
impl TimeUnit {
fn from_u8(n: u8) -> Result<TimeUnit, Error> {
match n {
0 => Ok(TimeUnit::Normalized),
1 => Ok(TimeUnit::Day),
2 => Ok(TimeUnit::Week),
_ => Err(Error::PofTimeUnit(n)),
}
}
}
#[derive(Clone, Copy, Debug)]
pub enum TimeInfo {
Gps,
Utc,
Unknown,
}
impl TimeInfo {
fn from_u8(n: u8) -> Result<TimeInfo, Error> {
match n {
0 => Ok(TimeInfo::Gps),
1 => Ok(TimeInfo::Utc),
2 => Ok(TimeInfo::Unknown),
_ => Err(Error::PofTimeInfo(n)),
}
}
}
impl<R: Debug + Seek + Read> Source for Reader<R> {
fn source(&mut self) -> Result<Option<Point>, Error> {
self.read_point()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn header() {
let reader = Reader::from_path("data/sbet_mission_1.pof").unwrap();
assert_eq!(Version::new(1, 1), reader.version);
assert_eq!(2015, reader.year);
assert_eq!(4, reader.month);
assert_eq!(29, reader.day);
assert_eq!(1114521, reader.entries);
}
#[test]
fn point() {
let mut reader = Reader::from_path("data/sbet_mission_1.pof").unwrap();
let point = reader.read_point().unwrap().unwrap();
assert_eq!(5.380900320500246e4, point.time);
assert_eq!(-107.8941420696491, point.longitude.to_degrees());
assert_eq!(3.852696630463423e1, point.latitude.to_degrees());
assert_eq!(1721.1666764324254, point.altitude);
assert_eq!(-3.5218866203789795e-1, point.roll.to_degrees());
assert_eq!(2.3209047516182637, point.pitch.to_degrees());
assert_eq!(359.62872162328546, point.yaw.to_degrees());
assert_eq!(0.0, point.distance.unwrap());
}
#[test]
fn iter() {
let reader = Reader::from_path("data/sbet_mission_1.pof").unwrap();
let points: Vec<_> = reader.into_iter().collect();
assert_eq!(1114521, points.len());
}
}