unbounded_gpsd/
lib.rs

1//! A small crate to interface with gpsd, based on the server JSON protocol.
2//!
3//! This crate uses the [log](https://crates.io/crates/log) crate for debug logging.
4//! Logs will only appear if the logging apparatus is correctly configured. As such,
5//! if you're filing an issue, we would appreciate it if you did this and gave us the
6//! relevant logs!
7
8extern crate serde;
9#[macro_use] extern crate serde_derive;
10#[macro_use] extern crate serde_json;
11extern crate chrono;
12#[macro_use] extern crate error_chain;
13#[macro_use] extern crate log;
14
15use std::net::{ToSocketAddrs, TcpStream};
16use std::io::{BufRead, BufReader, Write};
17use std::time::Duration;
18
19pub mod errors {
20    //! Error handling, using error-chain.
21
22    error_chain! {
23        types {
24            Error, ErrorKind, ResultExt, GpsdResult;
25        }
26        foreign_links {
27            Io(::std::io::Error);
28            Serde(::serde_json::Error);
29        }
30        errors {
31            DeserFailed(s: String, e: ::serde_json::Error) {
32                display("failed to deserialize text '{}': {}", s, e)
33            }
34            GpsdFailed(s: String) {
35                display("gpsd connection closed")
36            }
37        }
38    }
39}
40pub use errors::GpsdResult;
41pub mod types;
42#[cfg(test)]
43pub mod tests;
44use types::*;
45
46/// A connection to gpsd.
47pub struct GpsdConnection {
48    raw_data: bool,
49    inner: BufReader<TcpStream>
50}
51impl GpsdConnection {
52    /// Make a new connection to a given address.
53    pub fn new<A: ToSocketAddrs>(addr: A) -> GpsdResult<Self> {
54        let stream = TcpStream::connect(addr)?;
55        let inner = BufReader::new(stream);
56        Ok(Self { inner, raw_data: false })
57    }
58    /// Enable or disable watcher mode.
59    fn _watch(&mut self, watch: bool, json: bool, raw: u8) -> GpsdResult<()> {
60        let stream = self.inner.get_mut();
61        let watch_data = json!({
62            "class": "WATCH",
63            "enable": watch,
64            "json": json,
65            "raw": raw,
66        });
67        self.raw_data = raw > 0;
68        let msg = format!("?WATCH={}\n", watch_data.to_string());
69        stream.write_all(msg.as_bytes())?;
70        Ok(())
71    }
72    /// Enable or disable watcher mode.
73    pub fn watch(&mut self, watch: bool) -> GpsdResult<()> {
74        self._watch(watch, true, 0)
75    }
76    /// Enable RAW mode. In RAW mode, gpsd sends raw data from the GPS device, depending on the value of `raw`:
77    ///
78    /// When this attribute is set to 1 for a channel, gpsd reports the unprocessed NMEA or
79    /// AIVDM data stream from whatever device is attached. Binary GPS packets are hex-dumped.
80    /// RTCM2 and RTCM3 packets are not dumped in raw mode. When this attribute is set to 2 for a channel that
81    /// processes binary data, gpsd reports the received data verbatim without hex-dumping.
82    pub fn watch_raw(&mut self, watch: bool, json: bool, raw: u8) -> GpsdResult<()> {
83        self._watch(watch, json, raw)
84    }
85    /// The POLL command requests data from the last-seen fixes on all active
86    /// GPS devices. Devices must previously have been activated by ?WATCH to be
87    /// pollable.
88    pub fn poll(&mut self) -> GpsdResult<()> {
89        let stream = self.inner.get_mut();
90        stream.write_all("?POLL;\n".as_bytes())?;
91        Ok(())
92    }
93    /// Ask for the server's version (triggers a Response::Version).
94    pub fn version(&mut self) -> GpsdResult<()> {
95        let stream = self.inner.get_mut();
96        stream.write_all("?VERSION;\n".as_bytes())?;
97        Ok(())
98    }
99    /// Ask for the server's devices (triggers a Response::Devices)
100    pub fn devices(&mut self) -> GpsdResult<()> {
101        let stream = self.inner.get_mut();
102        stream.write_all("?DEVICES;\n".as_bytes())?;
103        Ok(())
104    }
105    /// Sets the read timeout for `get_response`.
106    ///
107    /// A value of `None` implies that the read will never block.
108    pub fn set_read_timeout(&mut self, dur: Option<Duration>) -> GpsdResult<()> {
109        self.inner.get_ref().set_read_timeout(dur)?;
110        Ok(())
111    }
112    /// Polls for responses from GPSD, blocking if necessary.
113    ///
114    /// Ideally, you run this in a loop somewhere to process messages.
115    pub fn get_response(&mut self) -> GpsdResult<Response> {
116        loop {
117            let mut buf = String::new();
118            if self.inner.read_line(&mut buf)? == 0 {
119                bail!(errors::ErrorKind::GpsdFailed(String::from("Gpsd Connection Closed")));
120            }
121
122            if buf == "" {
123                debug!("empty line received from GPSD");
124                continue;
125            }
126            debug!("raw GPSD data: {}", buf);
127            let data = serde_json::from_str(&buf);
128            debug!("serde output: {:?}", data);
129            match data {
130                Err(e) => {
131                    if self.raw_data {
132                        return Ok(Response::Raw(buf))
133                    } else {
134                        debug!("deserializing response failed: {:?}", e);
135                        bail!(errors::ErrorKind::DeserFailed(buf, e));
136                    }
137                },
138                Ok(x) => return Ok(x)
139            }
140        }
141    }
142}