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