pub trait FieldHeader {
fn name(&self) -> &'static str;
fn field_name_index(&self) -> i8;
fn is_signed(&self) -> u8;
fn predict(&self) -> u8;
fn encode(&self) -> u8;
}
impl FieldHeader for SimpleFieldDefinition {
fn name(&self) -> &'static str {
self.name
}
fn field_name_index(&self) -> i8 {
self.field_name_index
}
fn is_signed(&self) -> u8 {
self.is_signed
}
fn predict(&self) -> u8 {
self.predict
}
fn encode(&self) -> u8 {
self.encode
}
}
impl FieldHeader for ConditionalFieldDefinition {
fn name(&self) -> &'static str {
self.name
}
fn field_name_index(&self) -> i8 {
self.field_name_index
}
fn is_signed(&self) -> u8 {
self.is_signed
}
fn predict(&self) -> u8 {
self.predict
}
fn encode(&self) -> u8 {
self.encode
}
}
impl FieldHeader for MainFieldDefinition {
fn name(&self) -> &'static str {
self.name
}
fn field_name_index(&self) -> i8 {
self.field_name_index
}
fn is_signed(&self) -> u8 {
self.is_signed
}
fn predict(&self) -> u8 {
self.i_predict
}
fn encode(&self) -> u8 {
self.i_encode
}
}
#[allow(unused)]
pub fn write_slow_fields_header(writer: &mut SliceWriter) -> usize {
write_simple_header(writer, 'S', &BLACKBOX_SLOW_FIELDS);
writer.pos
}
#[allow(unused)]
pub fn write_conditional_header(
writer: &mut dyn BlackboxWriter,
frame_type: char,
fields: &[ConditionalFieldDefinition],
conditions: BitSet64,
) {
let filter = |f: &ConditionalFieldDefinition| conditions.test(f.condition);
write_common_field_lines(writer, frame_type, fields, &filter);
}
pub fn write_main_header(writer: &mut dyn BlackboxWriter, fields: &[MainFieldDefinition], conditions: BitSet64) {
let filter = |f: &MainFieldDefinition| conditions.test(f.condition);
write_common_field_lines(writer, 'I', fields, &filter);
let filtered = fields.iter().filter(|&f| filter(f));
write_field_line(writer, 'P', "predictor", filtered, |w, f| {
w.write_u8_ascii(f.p_predict);
});
let filtered = fields.iter().filter(|&f| filter(f));
write_field_line(writer, 'P', "encoding", filtered, |w, f| {
w.write_u8_ascii(f.p_encode);
});
}
pub fn write_simple_header(writer: &mut dyn BlackboxWriter, frame_type: char, fields: &[SimpleFieldDefinition]) {
write_common_field_lines(writer, frame_type, fields, |_f| true);
}
pub fn write_common_field_lines<'a, T, P>(
writer: &mut dyn BlackboxWriter,
frame_type: char,
fields: &[T],
mut predicate: P,
) where
T: FieldHeader + 'a,
P: FnMut(&T) -> bool, {
write_field_line(writer, frame_type, "name", fields.iter().filter(|&f| predicate(f)), |w, f| {
w.write_str(f.name());
let index = f.field_name_index();
if index >= 0 {
w.write_char('[');
w.write_u8_ascii(index.cast_unsigned());
w.write_char(']');
}
});
write_field_line(writer, frame_type, "signed", fields.iter().filter(|&f| predicate(f)), |w, f| {
w.write_u8_ascii(f.is_signed());
});
write_field_line(writer, frame_type, "predictor", fields.iter().filter(|&f| predicate(f)), |w, f| {
w.write_u8_ascii(f.predict());
});
write_field_line(writer, frame_type, "encoding", fields.iter().filter(|&f| predicate(f)), |w, f| {
w.write_u8_ascii(f.encode());
});
}
#[allow(unused)]
pub fn write_header(writer: &mut SliceWriter, conditions: BitSet64) -> usize {
writer.write_str("H Product:Blackbox flight data recorder by Nicholas Sherlock\n");
writer.write_str("H Data version:2\n");
write_main_header(writer, BLACKBOX_MAIN_FIELDS, conditions);
write_simple_header(writer, 'S', &BLACKBOX_SLOW_FIELDS);
writer.write_str("H Firmware type:Cleanflight\n");
writer.write_str("H Firmware revision:Betaflight 3.3.1 (611bc70f8) REVOLT\n");
writer.write_str("H Firmware date:Mar 21 2018 00:00:00\n");
writer.write_str("H Log start datetime:0000-01-01T00:00:00.000+00:00\n");
writer.write_str("H Craft name:Protea\n");
writer.write_str("H I interval:256\n");
writer.write_str("H P interval:1/8\n");
writer.write_str("H looptime:125\n");
writer.write_str("H gyro_sync_denom:1\n");
writer.write_str("H pid_process_denom:1\n");
writer.write_str("H gyro_scale:0x3f800000\n");
writer.write_str("H acc_1G:4096\n");
writer.write_str("H features:541130760\n");
writer.write_str("H debug_mode:0\n");
writer.write_str("H minthrottle:1070\n");
writer.write_str("H maxthrottle:2000\n");
writer.write_str("H motorOutput:158,2047\n");
writer.write_str("H vbat_scale:110\n");
writer.write_str("H vbatcellvoltage:33,35,43\n");
writer.write_str("H vbatref:113\n");
writer.write_str("H currentSensor:0,235\n");
writer.pos
}
#[cfg(test)]
mod tests {
#[test]
fn main_fields_header() {
let mut buffer = [0u8; 2048];
let mut writer = MockWriter { buffer: &mut buffer, pos: 0 };
let mut conditions = BitSet64::new();
_ = conditions.set(FieldCondition::ALWAYS);
_ = conditions.set(FieldCondition::AT_LEAST_MOTORS_1);
_ = conditions.set(FieldCondition::AT_LEAST_MOTORS_2);
_ = conditions.set(FieldCondition::AT_LEAST_MOTORS_3);
_ = conditions.set(FieldCondition::AT_LEAST_MOTORS_4);
_ = conditions.set(FieldCondition::PID);
_ = conditions.set(FieldCondition::PID_K);
_ = conditions.set(FieldCondition::PID_D_PITCH);
_ = conditions.set(FieldCondition::PID_D_ROLL);
_ = conditions.set(FieldCondition::GYRO);
_ = conditions.set(FieldCondition::RC_COMMANDS);
_ = conditions.set(FieldCondition::BATTERY_CURRENT);
_ = conditions.set(FieldCondition::BATTERY_VOLTAGE);
write_main_header(&mut writer, BLACKBOX_MAIN_FIELDS, conditions);
#[allow(clippy::unwrap_used)]
let result = core::str::from_utf8(&writer.buffer[..writer.pos]).unwrap();
assert!(result.contains("H Field I name:loopIteration,time"));
}
#[test]
fn main_header() {
use crate::blackbox::{Blackbox, BlackboxConfig};
let mut buffer = [0u8; 2048];
let mut writer = SliceWriter { buffer: &mut buffer, pos: 0 };
let mut blackbox = Blackbox::new();
let config = BlackboxConfig::new();
blackbox.init(config);
let _pos = write_header(&mut writer, blackbox.ctx.conditions);
#[allow(clippy::unwrap_used)]
let result = core::str::from_utf8(&writer.buffer[..writer.pos]).unwrap();
assert!(result.contains("H Field I name:loopIteration,time"));
}
#[test]
fn slow_fields_header() {
let mut buffer = [0u8; 1024];
let mut writer = MockWriter { buffer: &mut buffer, pos: 0 };
write_simple_header(&mut writer, 'S', &BLACKBOX_SLOW_FIELDS);
#[allow(clippy::unwrap_used)]
let result = core::str::from_utf8(&writer.buffer[..writer.pos]).unwrap();
assert!(result.contains(
"H Field S name:flight_mode_flags,state_flags,failsafe_phase,rx_signal_received,rx_flight_channel_is_valid"
));
assert!(result.contains("H Field S predictor:0,0,0,0,0"));
assert!(result.contains("H Field S encoding:1,1,7,7,7"));
}
}
#[cfg(test)]
mod tests {
}
#![allow(unused)]
use crate::{GpsState, MainState, SlowState};
#[allow(clippy::struct_field_names)]
#[derive(Clone, Copy, Debug)]
pub struct BlackboxCallbacks {
pub load_main_state: fn(&mut MainState, u32),
pub load_slow_state: fn(&mut SlowState),
pub load_gps_state: fn(&mut GpsState),
}
impl Default for BlackboxCallbacks {
fn default() -> Self {
Self { load_main_state: |_, _| {}, load_slow_state: |_| {}, load_gps_state: |_| {} }
}
}