blackbox_log/lib.rs
1#![no_std]
2#![warn(clippy::std_instead_of_alloc, clippy::std_instead_of_core)]
3#![warn(unreachable_pub, clippy::missing_panics_doc)]
4
5//! Ergonomic parser for Betaflight blackbox logs.
6//!
7//! For details about the format of blackbox logs, see the *Blackbox Internals*
8//! development documentation from [INAV][inav-doc], [Betaflight][bf-doc]
9//!
10//! # Examples
11//!
12//! The simplest way to extract a few fields of interest:
13//!
14//! ```
15//! use blackbox_log::frame::FieldDef;
16//! use blackbox_log::prelude::*;
17//! use blackbox_log::Filter;
18//!
19//! let filters = blackbox_log::FilterSet {
20//! // This restricts the included fields to `rcCommand[0]` through `rcCommand[3]`
21//! main: Filter::OnlyFields(["rcCommand"].into()),
22//! // ... only `flightModeFlags` for slow frames
23//! slow: Filter::OnlyFields(["flightModeFlags"].into()),
24//! // ... and no filter for gps frames -- include all fields
25//! gps: Filter::Unfiltered,
26//! };
27//!
28//! let file = b"...";
29//! for headers in blackbox_log::File::new(file).iter() {
30//! let headers = headers.expect("valid log headers");
31//!
32//! let mut parser = headers.data_parser_with_filters(&filters);
33//! while let Some(event) = parser.next() {
34//! match event {
35//! ParserEvent::Main(main) => {
36//! for (value, FieldDef { name, .. }) in
37//! main.iter().zip(headers.main_frame_def().iter())
38//! {
39//! println!("{name}: {value:?}");
40//! }
41//! }
42//! ParserEvent::Slow(slow) => {
43//! for (value, FieldDef { name, .. }) in
44//! slow.iter().zip(headers.slow_frame_def().iter())
45//! {
46//! println!("{name}: {value:?}");
47//! }
48//! }
49//! ParserEvent::Event(_) | ParserEvent::Gps(_) => {}
50//! }
51//! }
52//! }
53//! ```
54//!
55//! Get only the GPS data without parsing logs that cannot contain GPS frames:
56//!
57//! ```
58//! use blackbox_log::frame::FieldDef;
59//! use blackbox_log::prelude::*;
60//!
61//! let file = b"...";
62//! for headers in blackbox_log::File::new(file).iter() {
63//! let headers = headers.expect("valid log headers");
64//!
65//! if let Some(gps_def) = &headers.gps_frame_def() {
66//! let mut parser = headers.data_parser();
67//!
68//! while let Some(event) = parser.next() {
69//! if let ParserEvent::Gps(gps) = event {
70//! for (value, FieldDef { name, .. }) in gps.iter().zip(gps_def.iter()) {
71//! println!("{name}: {value:?}");
72//! }
73//! }
74//! }
75//! }
76//! }
77//! ```
78//!
79//! # Features
80//!
81//! - `std`: **Enabled** by default
82//!
83//! [bf-doc]: https://betaflight.com/docs/development/Blackbox-Internals
84//! [inav-doc]: https://github.com/iNavFlight/inav/blob/master/docs/development/Blackbox%20Internals.md
85
86extern crate alloc;
87#[cfg(feature = "std")]
88extern crate std;
89
90#[macro_use]
91mod utils;
92
93pub mod data;
94pub mod event;
95mod file;
96mod filter;
97pub mod frame;
98pub mod headers;
99mod parser;
100mod predictor;
101pub mod prelude;
102mod reader;
103pub mod units;
104
105use core::ops::Range;
106
107pub use self::data::{DataParser, ParserEvent};
108pub use self::event::Event;
109pub use self::file::File;
110pub use self::filter::{FieldFilter, Filter, FilterSet};
111pub use self::frame::{Unit, Value};
112use self::headers::FirmwareVersion;
113pub use self::headers::Headers;
114use self::reader::Reader;
115
116/// The first line of any blackbox log.
117const MARKER: &[u8] = b"H Product:Blackbox flight data recorder by Nicholas Sherlock\n";
118
119const BETAFLIGHT_SUPPORT: Range<FirmwareVersion> =
120 FirmwareVersion::new(4, 2, 0)..FirmwareVersion::new(4, 6, 0);
121const INAV_SUPPORT: Range<FirmwareVersion> =
122 FirmwareVersion::new(5, 0, 0)..FirmwareVersion::new(9, 0, 0);