Skip to content

Commit

Permalink
Merge pull request #41 from patrickoppel/no_std
Browse files Browse the repository at this point in the history
`no_std`
  • Loading branch information
elpiel committed Aug 10, 2022
2 parents 9241043 + b3b4370 commit 8a90195
Show file tree
Hide file tree
Showing 7 changed files with 324 additions and 256 deletions.
6 changes: 4 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ authors = [
description = "Simple NMEA 0183 parser"
license = "Apache-2.0"
keywords = ["NMEA", "gps", "glonass", "coordinate", "position"]
categories = ["parser-implementations"]
categories = ["parser-implementations", "no_std", "embedded"]
repository = "https://github.com/AeroRust/nmea"
documentation = "https://docs.rs/nmea/"
readme = "README.md"
Expand All @@ -20,13 +20,15 @@ all-features = true
rustdoc-args = ["--cfg", "docsrs"]

[dependencies]
nom = { version = "7.1.1", default-features = false, features = ["alloc"] }
nom = { version = "7.1.1", default-features = false }
chrono = { version = "0.4.19", default-features = false }
arrayvec = { version = "0.7.2", default-features = false }
heapless = "0.7.15"

[dev-dependencies]
quickcheck = { version = "1.0.3", default-features = false }
approx = "0.5.1"
pretty_assertions = "1"
doc-comment = "0.3"

[features]
Expand Down
65 changes: 30 additions & 35 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//
#![cfg_attr(not(any(feature = "std", test)), no_std)]

// nom::multi::many0
extern crate alloc;
use chrono::{NaiveDate, NaiveTime};
use core::convert::TryInto;
use core::{fmt, mem, ops::BitOr};
use std::{collections::VecDeque, convert::TryInto};
use heapless::{Deque, Vec};

mod parse;
mod sentences;
Expand Down Expand Up @@ -72,7 +72,7 @@ pub struct Nmea {
pub pdop: Option<f32>,
/// Geoid separation in meters
pub geoid_separation: Option<f32>,
pub fix_satellites_prns: Option<Vec<u32>>,
pub fix_satellites_prns: Option<Vec<u32, 12>>,
satellites_scan: [SatsPack; GnssType::COUNT],
required_sentences_for_nav: SentenceMask,
last_fix_time: Option<NaiveTime>,
Expand All @@ -82,7 +82,13 @@ pub struct Nmea {

#[derive(Debug, Clone, Default)]
struct SatsPack {
data: VecDeque<[Option<Satellite>; 4]>,
/// max number of visible GNSS satellites per hemisphere, assuming global coverage
/// GPS: 16
/// GLONASS: 12
/// BeiDou: 12 + 3 IGSO + 3 GEO
/// Galileo: 12
/// => 58 total Satellites => max 15 rows of data
data: Deque<Vec<Option<Satellite>, 4>, 15>,
max_len: usize,
}

Expand Down Expand Up @@ -155,16 +161,18 @@ impl<'a> Nmea {
}
}

/// Returns used sattelites
pub fn satellites(&self) -> Vec<Satellite> {
let mut ret = Vec::with_capacity(20);
/// Returns used satellites
pub fn satellites(&self) -> Vec<Satellite, 58> {
let mut ret = Vec::<Satellite, 58>::new();
let sat_key = |sat: &Satellite| (sat.gnss_type() as u8, sat.prn());
for sns in &self.satellites_scan {
for sat_pack in sns.data.iter().rev() {
for sat in sat_pack.iter().flatten() {
// for sat_pack in sns.data.iter().rev() {
for sat_pack in sns.data.iter().rev().flatten() {
for sat in sat_pack.iter() {
match ret.binary_search_by_key(&sat_key(sat), sat_key) {
Ok(_pos) => {} //already setted
Err(pos) => ret.insert(pos, sat.clone()),
//already set
Ok(_pos) => {}
Err(pos) => ret.insert(pos, sat.clone()).unwrap(),
}
}
}
Expand All @@ -191,8 +199,9 @@ impl<'a> Nmea {
.try_into()
.map_err(|_| NmeaError::InvalidGsvSentenceNum)?;
d.max_len = full_pack_size.max(d.max_len);

d.data.push_back(data.sats_info);
d.data
.push_back(data.sats_info)
.expect("Should not get the more than expected number of satellites");
if d.data.len() > d.max_len {
d.data.pop_front();
}
Expand Down Expand Up @@ -425,18 +434,10 @@ impl fmt::Display for Nmea {
write!(
f,
"{}: lat: {} lon: {} alt: {} {:?}",
self.fix_time
.map(|l| format!("{:?}", l))
.unwrap_or_else(|| "None".to_owned()),
self.latitude
.map(|l| format!("{:3.8}", l))
.unwrap_or_else(|| "None".to_owned()),
self.longitude
.map(|l| format!("{:3.8}", l))
.unwrap_or_else(|| "None".to_owned()),
self.altitude
.map(|l| format!("{:.3}", l))
.unwrap_or_else(|| "None".to_owned()),
format_args!("{:?}", self.fix_time),
format_args!("{:?}", self.latitude),
format_args!("{:?}", self.longitude),
format_args!("{:?}", self.altitude),
self.satellites()
)
}
Expand Down Expand Up @@ -482,15 +483,9 @@ impl fmt::Display for Satellite {
"{}: {} elv: {} ath: {} snr: {}",
self.gnss_type,
self.prn,
self.elevation
.map(|e| format!("{}", e))
.unwrap_or_else(|| "--".to_owned()),
self.azimuth
.map(|e| format!("{}", e))
.unwrap_or_else(|| "--".to_owned()),
self.snr
.map(|e| format!("{}", e))
.unwrap_or_else(|| "--".to_owned())
format_args!("{:?}", self.elevation),
format_args!("{:?}", self.azimuth),
format_args!("{:?}", self.snr),
)
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/sentences/bwc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ fn do_parse_bwc(i: &[u8]) -> Result<BwcData, NmeaError> {
let (i, _) = char(',')(i)?;

// 12. Waypoint ID
let (_i, waypoint_id) = opt(map_res(is_not(",*"), std::str::from_utf8))(i)?;
let (_i, waypoint_id) = opt(map_res(is_not(",*"), core::str::from_utf8))(i)?;

// 13. FAA mode indicator (NMEA 2.3 and later, optional)

Expand Down
65 changes: 56 additions & 9 deletions src/sentences/gsa.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
use heapless::Vec;
use nom::branch::alt;
use nom::bytes::complete::take_while1;
use nom::character::complete::{char, one_of};
use nom::combinator::{all_consuming, opt, value};
use nom::multi::many0;
use nom::error::ErrorKind;
use nom::error::ParseError;
use nom::number::complete::float;
use nom::sequence::terminated;
use nom::Err;
use nom::IResult;
use nom::InputLength;
use nom::Parser;

use crate::parse::NmeaSentence;
use crate::{sentences::utils::number, NmeaError};
Expand All @@ -27,17 +32,49 @@ pub enum GsaMode2 {
pub struct GsaData {
pub mode1: GsaMode1,
pub mode2: GsaMode2,
pub fix_sats_prn: Vec<u32>,
pub fix_sats_prn: Vec<u32, 12>,
pub pdop: Option<f32>,
pub hdop: Option<f32>,
pub vdop: Option<f32>,
}

fn gsa_prn_fields_parse(i: &[u8]) -> IResult<&[u8], Vec<Option<u32>>> {
/// This function is take from `nom`, see `nom::multi::many0`
/// with one difference - we use a [`heapless::Vec`]
/// because we want `no_std` & no `alloc`
fn many0<I, O, E, F>(mut f: F) -> impl FnMut(I) -> IResult<I, Vec<O, 12>, E>
where
I: Clone + InputLength,
F: Parser<I, O, E>,
E: ParseError<I>,
O: core::fmt::Debug,
{
move |mut i: I| {
// let mut acc = crate::lib::std::vec::Vec::with_capacity(4);
let mut acc = Vec::<_, 12>::new();
loop {
let len = i.input_len();
match f.parse(i.clone()) {
Err(Err::Error(_)) => return Ok((i, acc)),
Err(e) => return Err(e),
Ok((i1, o)) => {
// infinite loop check: the parser must always consume
if i1.input_len() == len {
return Err(Err::Error(E::from_error_kind(i, ErrorKind::Many0)));
}

i = i1;
acc.push(o).unwrap();
}
}
}
}
}

fn gsa_prn_fields_parse(i: &[u8]) -> IResult<&[u8], Vec<Option<u32>, 12>> {
many0(terminated(opt(number::<u32>), char(',')))(i)
}

type GsaTail = (Vec<Option<u32>>, Option<f32>, Option<f32>, Option<f32>);
type GsaTail = (Vec<Option<u32>, 12>, Option<f32>, Option<f32>, Option<f32>);

fn do_parse_gsa_tail(i: &[u8]) -> IResult<&[u8], GsaTail> {
let (i, prns) = gsa_prn_fields_parse(i)?;
Expand Down Expand Up @@ -80,7 +117,16 @@ fn do_parse_gsa(i: &[u8]) -> IResult<&[u8], GsaData> {
'3' => GsaMode2::Fix3D,
_ => unreachable!(),
},
fix_sats_prn: tail.0.drain(..).flatten().collect(),
fix_sats_prn: {
let mut fix_sats_prn = Vec::<u32, 12>::new();
for sat in tail.0.iter().flatten() {
fix_sats_prn.push(*sat).unwrap()
}
// now that we don't have `drain()` from `std::Vec`,
// we clear the `heapless::Vec`'s tail manually
tail.0.clear();
fix_sats_prn
},
pdop: tail.1,
hdop: tail.2,
vdop: tail.3,
Expand Down Expand Up @@ -148,12 +194,13 @@ mod tests {
#[test]
fn test_gsa_prn_fields_parse() {
let (_, ret) = gsa_prn_fields_parse(b"5,").unwrap();
assert_eq!(vec![Some(5)], ret);
assert_eq!(ret, &[Some(5)]);

let (_, ret) = gsa_prn_fields_parse(b",").unwrap();
assert_eq!(vec![None], ret);
assert_eq!(ret, &[None]);

let (_, ret) = gsa_prn_fields_parse(b",,5,6,").unwrap();
assert_eq!(vec![None, None, Some(5), Some(6)], ret);
assert_eq!(ret, &[None, None, Some(5), Some(6)],);
}

#[test]
Expand All @@ -164,7 +211,7 @@ mod tests {
GsaData {
mode1: GsaMode1::Automatic,
mode2: GsaMode2::Fix3D,
fix_sats_prn: vec![16, 18, 22, 24],
fix_sats_prn: Vec::<_, 12>::from_slice(&[16, 18, 22, 24]).unwrap(),
pdop: Some(3.6),
hdop: Some(2.1),
vdop: Some(2.2),
Expand Down
21 changes: 15 additions & 6 deletions src/sentences/gsv.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use heapless::Vec;
use nom::character::complete::char;
use nom::combinator::{cond, opt, rest_len};
use nom::IResult;
Expand All @@ -12,7 +13,8 @@ pub struct GsvData {
pub number_of_sentences: u16,
pub sentence_num: u16,
pub _sats_in_view: u16,
pub sats_info: [Option<Satellite>; 4],
// see SatPack in lib.rs
pub sats_info: Vec<Option<Satellite>, 4>,
}

fn parse_gsv_sat_info(i: &[u8]) -> IResult<&[u8], Satellite> {
Expand Down Expand Up @@ -43,18 +45,25 @@ fn do_parse_gsv(i: &[u8]) -> IResult<&[u8], GsvData> {
let (i, _) = char(',')(i)?;
let (i, _sats_in_view) = number::<u16>(i)?;
let (i, _) = char(',')(i)?;
let (i, sat0) = opt(parse_gsv_sat_info)(i)?;
let (i, sat1) = opt(parse_gsv_sat_info)(i)?;
let (i, sat2) = opt(parse_gsv_sat_info)(i)?;
let (i, sat3) = opt(parse_gsv_sat_info)(i)?;
let sats = Vec::<Option<Satellite>, 4>::new();

// We loop through the indices and parse the satellite data
let (i, sats) = (0..4).try_fold((i, sats), |(i, mut sats), sat_index| {
let (i, sat) = opt(parse_gsv_sat_info)(i)?;

sats.insert(sat_index, sat).unwrap();

Ok((i, sats))
})?;

Ok((
i,
GsvData {
gnss_type: GnssType::Galileo,
number_of_sentences,
sentence_num,
_sats_in_view,
sats_info: [sat0, sat1, sat2, sat3],
sats_info: sats,
},
))
}
Expand Down
Loading

0 comments on commit 8a90195

Please sign in to comment.