#![allow(missing_debug_implementations)]
use std::{
fmt,
io::{self, Read, Write},
str::FromStr,
sync::Mutex,
thread,
time::{Duration, Instant},
};
use chrono::{DateTime, Utc};
use failure::{Error, ResultExt};
use lazy_static::lazy_static;
use log::{info, warn};
use sysfs_gpio::Direction;
use tokio::{
self,
codec::{Decoder, LinesCodec},
prelude::{Future, Stream},
};
use tokio_serial::{Serial, SerialPortSettings};
use crate::{config::CONFIG, error};
lazy_static! {
pub static ref GPS: Mutex<Gps> = Mutex::new(Gps::default());
}
#[derive(Debug, Default)]
pub struct Gps {
latest_data: Option<Frame>,
}
impl Gps {
pub fn initialize(&mut self) -> Result<(), Error> {
info!("Initializing GPS\u{2026}");
CONFIG
.gps()
.power_gpio()
.set_direction(Direction::Out)
.context(error::Gps::Init)?;
if self.is_on().context(error::Gps::Init)? {
info!("GPS is on, turning off for 2 seconds for stability");
self.turn_off().context(error::Gps::Init)?;
thread::sleep(Duration::from_secs(2))
}
info!("Turning GPS on\u{2026}");
self.turn_on().context(error::Gps::Init)?;
info!("GPS on.");
info!("Starting serial connection\u{2026}");
let mut serial_settings = SerialPortSettings::default();
serial_settings.baud_rate = CONFIG.gps().baud_rate();
let mut serial =
Serial::from_path(CONFIG.gps().uart(), &serial_settings).context(error::Gps::Init)?;
serial.set_exclusive(false).context(error::Gps::Init)?;
info!("Serial connection started.");
info!("Sending configuration frames\u{2026}");
let messages = [
vec![
0xB5, 0x62, 0x06, 0x08, 0x06, 0x00, 0x64, 0x00, 0x01, 0x00, 0x01, 0x00, 0x7A, 0x12,
],
vec![0xB5, 0x62, 0x05, 0x01, 0x02, 0x00, 0x06, 0x01, 0x0F, 0x38],
vec![
0xB5, 0x62, 0x06, 0x01, 0x03, 0x00, 0xF0, 0x01, 0x00, 0xFB, 0x11,
],
vec![
0xB5, 0x62, 0x06, 0x01, 0x03, 0x00, 0xF0, 0x03, 0x00, 0xFD, 0x15,
],
vec![
0xB5, 0x62, 0x06, 0x01, 0x03, 0x00, 0xF0, 0x05, 0x00, 0xFF, 0x19,
],
];
for message in &messages {
for _ in 0..100 {
serial.write_all(message).context(error::Gps::Init)?;
thread::sleep(Duration::from_millis(10));
}
}
info!("Configuration frames sent");
info!("Setting GPS to airborne (<1g) mode");
if Gps::enter_airborne_1g_mode(&mut serial).is_ok() {
info!("GPS entered airborne (<1g) mode successfully");
} else {
warn!("GPS failed to enter airborne (<1g) mode");
}
let (_writer, reader) = LinesCodec::new_with_max_length(250)
.framed(serial)
.then(Self::parse_frame)
.split();
let processor = reader
.for_each(|frame| {
GPS.lock().unwrap().latest_data = if frame.is_valid() { Some(frame) } else { None };
Ok(())
})
.map_err(|e| warn!("Error processing frame: {}", e));
tokio::run(processor);
Ok(())
}
pub fn is_on(&self) -> Result<bool, Error> {
Ok(CONFIG.gps().power_gpio().get_value()? == 1)
}
pub fn turn_on(&self) -> Result<(), Error> {
if self.is_on()? {
warn!("Turning on the GPS but it was already on.");
} else {
CONFIG.gps().power_gpio().set_value(1)?
}
Ok(())
}
pub fn turn_off(&self) -> Result<(), Error> {
if self.is_on()? {
CONFIG.gps().power_gpio().set_value(0)?
} else {
warn!("Turning off the GPS but it was already off.");
}
Ok(())
}
fn enter_airborne_1g_mode(serial: &mut Serial) -> Result<(), Error> {
let msg = [
0xB5, 0x62, 0x06, 0x24, 0x24, 0x00,
0xFF, 0xFF, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0x05, 0x00,
0xFA, 0x00, 0xFA, 0x00, 0x64, 0x00, 0x2C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x16, 0xDC,
];
let mut ack = [
0xB5, 0x62, 0x05, 0x01, 0x02, 0x00, msg[2], msg[3], 0x00, 0x00,
];
for i in 2..8 {
ack[8] += ack[i];
ack[9] += ack[8];
}
let start = Instant::now();
while start.elapsed() < Duration::from_secs(6) {
serial.flush()?;
serial.write_all(&[0xFF])?;
thread::sleep(Duration::from_millis(500));
serial.write_all(&msg)?;
let mut checked_bytes = 0;
let ack_start = Instant::now();
while ack_start.elapsed() < Duration::from_secs(3) {
if checked_bytes == 10 {
return Ok(());
}
let mut byte = [0];
serial.read_exact(&mut byte)?;
if byte[0] == ack[checked_bytes] {
checked_bytes += 1;
} else {
checked_bytes = 0;
}
}
}
unimplemented!()
}
pub fn latest_data(&self) -> Option<Frame> {
self.latest_data
}
fn parse_frame(line: Result<String, io::Error>) -> Result<Frame, Error> {
let _line_str = line?;
unimplemented!()
}
}
impl Drop for Gps {
fn drop(&mut self) {
}
}
#[derive(Debug, Clone, Copy)]
pub struct Frame {
fix_time: DateTime<Utc>,
status: FixStatus,
satellites: u8,
latitude: f32,
longitude: f32,
altitude: f32,
pdop: f32,
hdop: f32,
vdop: f32,
speed: f32,
course: f32,
}
impl Frame {
pub fn fix_time(&self) -> DateTime<Utc> {
self.fix_time
}
pub fn status(&self) -> FixStatus {
self.status
}
pub fn is_valid(&self) -> bool {
self.status == FixStatus::Active
}
pub fn satellites(&self) -> u8 {
self.satellites
}
pub fn latitude(&self) -> f32 {
self.latitude
}
pub fn longitude(&self) -> f32 {
self.longitude
}
pub fn altitude(&self) -> f32 {
self.altitude
}
pub fn pdop(&self) -> f32 {
self.pdop
}
pub fn hdop(&self) -> f32 {
self.hdop
}
pub fn vdop(&self) -> f32 {
self.vdop
}
pub fn speed(&self) -> f32 {
self.speed
}
pub fn course(&self) -> f32 {
self.course
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FixStatus {
Active,
Void,
}
impl FromStr for FixStatus {
type Err = error::Gps;
fn from_str(s: &str) -> Result<FixStatus, Self::Err> {
match s {
"A" => Ok(FixStatus::Active),
"V" => Ok(FixStatus::Void),
_ => Err(error::Gps::InvalidStatus {
status: s.to_owned(),
}),
}
}
}
impl fmt::Display for FixStatus {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match *self {
FixStatus::Active => "A",
FixStatus::Void => "V",
}
)
}
}
#[cfg(test)]
mod tests {
use super::{FixStatus, GPS};
#[test]
fn gps_status_from_str() {
assert_eq!("A".parse::<FixStatus>().unwrap(), FixStatus::Active);
assert_eq!("V".parse::<FixStatus>().unwrap(), FixStatus::Void);
assert!("".parse::<FixStatus>().is_err());
assert!("invalid".parse::<FixStatus>().is_err());
assert!("a".parse::<FixStatus>().is_err());
assert!("Ab".parse::<FixStatus>().is_err());
}
#[test]
fn gps_status_display() {
assert_eq!(format!("{}", FixStatus::Active), "A");
assert_eq!(format!("{}", FixStatus::Void), "V");
}
#[test]
#[ignore]
fn gps_initialize() {
let mut gps = GPS.lock().unwrap();
gps.initialize().unwrap();
}
}