rtcm_rs/lib.rs
1//! # rtcm-rs
2//!
3//! `rtcm-rs` is a Rust library for decoding and encoding RTCM version 3 messages as defined in the RTCM Standard 10403.x.
4//! This library aims to provide an efficient, safe, and easy-to-use way to handle RTCM messages. Currently, the library
5//! supports a subset of the RTCM standard's messages, but we're working to extend the support to all messages.
6//!
7//! This library uses `#[forbid(unsafe_code)]` attribute, ensuring that all operations are safe from undefined behavior,
8//! data races, and many common bugs. Therefore, you can rely on `rtcm-rs` for not only its functionality but also its commitment to safety.
9//!
10//! The library also provides support for `serde`, a powerful serialization and deserialization framework, allowing users to convert RTCM
11//! messages into various formats such as JSON, XML and more. Furthermore, `rtcm-rs` is `no_std` compatible and doesn't rely
12//! on dynamic memory allocations, which makes it suitable for use in embedded systems.
13//!
14//! Here are some examples of how you can use `rtcm-rs`:
15//!
16//! ## Decoding a RTCM message
17//! ```no_run
18//! use rtcm_rs::prelude::*;
19//! use std::io::Read;
20//!
21//! # fn main() {
22//! let mut rtcm_file = std::fs::File::open("testdata/msg1001_3.rtcm").unwrap();
23//! let mut buffer = Vec::<u8>::new();
24//! rtcm_file.read_to_end(&mut buffer).unwrap();
25//!
26//! if let (_, Some(message_frame)) = next_msg_frame(buffer.as_slice()) {
27//! let msg = message_frame.get_message();
28//! println!("{:?}", msg);
29//! }
30//! # }
31//! ```
32//!
33//! ## Encoding a RTCM message
34//! ```no_run
35//! use rtcm_rs::prelude::*;
36//! use rtcm_rs::msg::{Msg1001T, Msg1001Sat};
37//! use rtcm_rs::util::DataVec;
38//!
39//! # fn main() {
40//! let mut message_builder = MessageBuilder::new();
41//!
42//! let result = message_builder.build_message(
43//! &Message::Msg1001(
44//! Msg1001T {
45//! reference_station_id: 100,
46//! gps_epoch_time_ms: 0,
47//! synchronous_gnss_msg_flag: 0,
48//! divergence_free_smoothing_flag: 0,
49//! smoothing_interval_index: 0,
50//! satellites: {
51//! let mut satellites = DataVec::new();
52//! satellites.push(Msg1001Sat {
53//! gps_satellite_id: 20,
54//! gps_l1_code_ind: 0,
55//! l1_pseudorange_m: Some(20000000.0),
56//! l1_phase_pseudorange_diff_m: Some(0.2),
57//! l1_lock_time_index: 0,
58//! });
59//! satellites.push(Msg1001Sat {
60//! gps_satellite_id: 21,
61//! gps_l1_code_ind: 0,
62//! l1_pseudorange_m: Some(26000000.0),
63//! l1_phase_pseudorange_diff_m: Some(0.4),
64//! l1_lock_time_index: 0,
65//! });
66//! satellites
67//! }}));
68//!
69//! if let Ok(bytes) = result {
70//! println!("Encoded message: {:?}", bytes);
71//! }
72//! # }
73//! ```
74//!
75//! For a full list of features and capabilities, see the [README](https://github.com/martinhakansson/rtcm-rs/blob/master/README.md).
76
77// #![no_std]
78#![cfg_attr(not(feature = "std"), no_std)]
79#![forbid(unsafe_code)]
80//use message::{Message, MessageBuilder};
81//use preamble::MessageFrame;
82use crc_any;
83#[cfg(feature = "serde")]
84use sd::{de::Visitor, Deserialize, Serialize};
85use tinyvec;
86
87#[cfg(feature = "test_gen")]
88mod source_repr;
89#[cfg(feature = "test_gen")]
90pub mod val_gen;
91
92mod df;
93pub mod msg;
94pub mod rtcm_error;
95pub mod util;
96
97pub use msg::message::{Message, MessageBuilder};
98
99mod message_frame;
100pub use message_frame::MessageFrame;
101
102pub mod prelude {
103 pub use crate::rtcm_error::RtcmError;
104 #[cfg(feature = "test_gen")]
105 pub use crate::source_repr::{SourceOutput, SourceRepr};
106 pub use crate::{next_msg_frame, Message, MessageBuilder, MessageFrame, MsgFrameIter};
107}
108
109/// next_msg_frame takes a slice of bytes and returns a tuple containing the number of bytes consumed, and a `MessageFrame` if one was found.
110///
111/// # Parameters
112///
113/// * `data` - A `&[u8]` to search for a `MessageFrame`
114///
115/// # Returns
116///
117/// A tuple containing the number of bytes consumed, and a `MessageFrame` if one was found.
118pub fn next_msg_frame(data: &[u8]) -> (usize, Option<MessageFrame>) {
119 for (i, b) in data.iter().enumerate() {
120 if *b == 0xd3 {
121 match MessageFrame::new(&data[i..]) {
122 Ok(m) => return (i + m.frame_len(), Some(m)),
123 Err(rtcm_error::RtcmError::Incomplete) => return (i, None),
124 Err(rtcm_error::RtcmError::NotValid) => {
125 continue;
126 }
127 _ => unreachable!(),
128 }
129 }
130 }
131 (data.len(), None)
132}
133
134/// MsgFrameIter is an iterator that returns `MessageFrame`s found in a `&[u8]`.
135pub struct MsgFrameIter<'a> {
136 data: &'a [u8],
137 index: usize,
138}
139impl<'a> MsgFrameIter<'a> {
140 /// Creates a new MsgFrameIter from a `&[u8]`
141 ///
142 /// # Parameters
143 ///
144 /// * `data` - A `&[u8]` to search for `MessageFrame`s
145 pub fn new(data: &'a [u8]) -> Self {
146 MsgFrameIter { data, index: 0 }
147 }
148 /// Returns the number of bytes consumed by the iterator so far
149 pub fn consumed(&self) -> usize {
150 self.index
151 }
152}
153impl<'a> core::iter::Iterator for &mut MsgFrameIter<'a> {
154 type Item = MessageFrame<'a>;
155
156 fn next(&mut self) -> Option<Self::Item> {
157 if self.index >= self.data.len() {
158 return None;
159 }
160 let (consumed, mf) = next_msg_frame(&self.data[self.index..]);
161 self.index += consumed;
162 mf
163 }
164}