#![warn(
missing_docs,
clippy::missing_docs_in_private_items,
clippy::missing_errors_doc,
clippy::missing_panics_doc
)]
#![feature(cursor_remaining)]
#![feature(buf_read_has_data_left)]
pub mod byteorder_gpmf;
use chrono::{DateTime, Utc};
use fixed::types::{I16F16, I32F32};
use num_enum::TryFromPrimitive;
use strum::{Display, EnumIter, EnumString, IntoEnumIterator};
use tracing::{debug, enabled, error, info, span, trace, warn, Level};
const DATE_FORMAT: &str = "%y%m%d%H%M%S%.3f";
pub enum Entry {
Simgle(KeyValue),
Seq(Vec<KeyValue>),
}
#[derive(Debug, Clone)]
pub struct KeyValue {
key: Tag,
value: Value,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, EnumIter, EnumString, Display, TryFromPrimitive)]
#[repr(u8)]
pub enum Type {
I8 = b'b',
U8 = b'B',
Char = b'c',
F64 = b'd',
F32 = b'f',
FourCC = b'F',
U128 = b'G',
I64 = b'j',
U64 = b'J',
I32 = b'l',
U32 = b'L',
Fixed32 = b'q',
Fixed64 = b'Q',
I16 = b's',
U16 = b'S',
Date = b'U',
Complex = b'?',
Nested = b'\0',
}
impl Type {
pub fn size(&self) -> usize {
match &self {
Type::I8 | Type::U8 | Type::Char => 1,
Type::I16 | Type::U16 => 2,
Type::F32 | Type::FourCC | Type::I32 | Type::U32 | Type::Fixed32 => 4,
Type::F64 | Type::I64 | Type::U64 | Type::Fixed64 => 8,
Type::U128 | Type::Date => 16,
Type::Complex => {
warn!("COMPLEX SIZE NOT KNOWN actually depends on previous TYPE definition");
1
}
Type::Nested => {
warn!("NESTED SIZE NOT KNOWN");
1
}
}
}
}
#[derive(Debug, PartialEq, EnumString, EnumIter, Display)]
pub enum Model {
Hero5,
Other(String),
}
#[derive(Debug, Clone)]
pub enum Value {
I8(i8),
U8(u8),
Char(char),
String(String),
F64(f64),
F32(f32),
Tag(Tag),
U128(u128),
I64(i64),
U64(u64),
I32(i32),
U32(u32),
Fixed32(I16F16),
Fixed64(I32F32),
I16(i16),
U16(u16),
Date(DateTime<Utc>),
Complex(Vec<Vec<Value>>),
Nested(Vec<KeyValue>),
Simple(Vec<Vec<Value>>),
Type(Vec<Type>),
Strings(Vec<String>),
}
impl Value {
pub fn datatype(&self) -> Type {
match self {
Value::I8(_) => Type::I8,
Value::U8(_) => Type::U8,
Value::Char(_) => Type::Char,
Value::String(_) => Type::Char,
Value::F64(_) => Type::F64,
Value::F32(_) => Type::F32,
Value::Tag(_) => Type::FourCC,
Value::U128(_) => Type::U128,
Value::I64(_) => Type::I64,
Value::U64(_) => Type::U64,
Value::I32(_) => Type::I32,
Value::U32(_) => Type::U32,
Value::Fixed32(_) => Type::Fixed32,
Value::Fixed64(_) => Type::Fixed64,
Value::I16(_) => Type::I16,
Value::U16(_) => Type::U16,
Value::Date(_) => Type::Date,
Value::Complex(_) => Type::Complex,
Value::Nested(_) => Type::Nested,
_ => unimplemented!(),
}
}
}
#[derive(Debug, Clone, PartialEq, EnumString, EnumIter, Display)]
pub enum Tag {
#[strum(serialize = "DEVC", to_string = "Device")]
DEVC,
#[strum(serialize = "DVID", to_string = "Device ID")]
DVID,
#[strum(serialize = "DVNM", to_string = "Device Name")]
DVNM,
#[strum(serialize = "STRM", to_string = "Stream")]
STRM,
#[strum(serialize = "STNM", to_string = "Stream Name")]
STNM,
#[strum(serialize = "RMRK", to_string = "Stream Comment")]
RMRK,
#[strum(serialize = "SCAL", to_string = "Scale Factor")]
SCAL,
#[strum(serialize = "SIUN", to_string = "SI Unit")]
SIUN,
#[strum(serialize = "UNIT", to_string = "Non SI Unit")]
UNIT,
#[strum(serialize = "TYPE", to_string = "Type")]
TYPE,
#[strum(serialize = "TSMP", to_string = "Total Samples")]
TSMP,
#[strum(serialize = "TIMO", to_string = "Time Offset")]
TIMO,
#[strum(serialize = "EMPT", to_string = "Empty Payload Count")]
EMPT,
#[strum(serialize = "TICK", to_string = "Start Timestamp")]
TICK,
#[strum(serialize = "TOCK", to_string = "End Timestamp")]
TOCK,
#[strum(serialize = "TMPC", to_string = "Temp")]
TMPC,
#[strum(serialize = "ACCL", to_string = "Accel")]
ACCL,
#[strum(serialize = "GYRO", to_string = "Gyro")]
GYRO,
#[strum(serialize = "ISOG", to_string = "Image Sensor Gain")]
ISOG,
#[strum(serialize = "SHUT", to_string = "Exposure Time")]
SHUT,
#[strum(serialize = "GPS5", to_string = "GPS 5")]
GPS5,
#[strum(serialize = "GPSU", to_string = "GPS UTC")]
GPSU,
#[strum(serialize = "GPSF", to_string = "GPS Fix")]
GPSF,
#[strum(serialize = "GPSP", to_string = "GPS DOP")]
GPSP,
#[strum(serialize = "MAGN", to_string = "Magnetometer")]
MAGN,
#[strum(serialize = "STMP", to_string = "Timestamp")]
STMP,
#[strum(serialize = "FACE", to_string = "Face Bbox")]
FACE,
#[strum(serialize = "FCNM", to_string = "Face count per Frame")]
FCNM,
#[strum(serialize = "ISOE", to_string = "Image Sensor Gain E")]
ISOE,
#[strum(serialize = "ALLD", to_string = "Auto Low Light frame Duration")]
ALLD,
#[strum(serialize = "WBAL", to_string = "White Balance in Kelvin")]
WBAL,
#[strum(serialize = "WRGB", to_string = "White Balance RGB gains")]
WRGB,
#[strum(serialize = "YAVG", to_string = "Luma (Y) Average over the frame")]
YAVG,
#[strum(serialize = "HUES", to_string = "Predominant hues over the frame")]
HUES,
#[strum(serialize = "UNIF", to_string = "Image uniformity")]
UNIF,
#[strum(serialize = "SCEN", to_string = "Scene classifier")]
SCEN,
#[strum(serialize = "SROT", to_string = "Sensor Read Out Time")]
SROT,
#[strum(serialize = "CORI", to_string = "Camera Orientation")]
CORI,
#[strum(serialize = "IORI", to_string = "Image Orientation")]
IORI,
#[strum(serialize = "GRAV", to_string = "Gravity Vector")]
GRAV,
#[strum(serialize = "WNDM", to_string = "Wind Processing")]
WNDM,
#[strum(serialize = "MWET", to_string = "Microphone is Wet")]
MWET,
#[strum(serialize = "AALP", to_string = "Audio Levels (dBFS)")]
AALP,
#[strum(serialize = "DISP", to_string = "Depth Map")]
DISP,
#[strum(serialize = "MSKP", to_string = "Main video frame skip")]
MSKP,
#[strum(serialize = "LSKP", to_string = "Low res video frame skip")]
LSKP,
#[strum(serialize = "GPS9", to_string = "GPS 9")]
GPS9,
#[strum(serialize = "HMMT", to_string = "HMMT UNDOCUMENTED HiLights ???")]
HMMT,
#[strum(serialize = "KBAT", to_string = "KBAT UNDOCUMENTED Battery Status ???")]
KBAT,
#[strum(default)]
Other(String),
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::Path;
use std::sync::Once;
use chrono::{TimeZone, Utc};
use fixed::prelude::*;
use fixed::types::{I16F16, I32F32};
use tracing::Level;
use tracing_subscriber::FmtSubscriber;
static INIT: Once = Once::new();
pub(crate) fn setup() {
INIT.call_once(|| {
let subscriber = FmtSubscriber::builder()
.with_max_level(Level::TRACE)
.finish();
tracing::subscriber::set_global_default(subscriber)
.expect("setting default subscriber failed");
});
}
#[test]
fn test_type() {
setup();
Type::iter().for_each(|t| {
info!("{} {:?}", t, t);
});
}
#[test]
fn test_file() -> anyhow::Result<()> {
let dir = Path::new("samples");
let path = dir.join("hero5.raw");
let text = std::fs::read(path).unwrap();
text.iter().enumerate().for_each(|(i, b)| {
print!("{}\t{}", i, b);
if b.is_ascii() {
println!("\t{}", *b as char);
} else {
println!()
}
});
Ok(())
}
#[test]
fn test_take_until() {
let data = [b'a', b'b', b'c', 0, b'd', 0];
let until_null: Vec<_> = data.into_iter().take_while(|b| *b != 0).collect();
let string = String::from_utf8(until_null).unwrap();
assert_eq!(string, String::from("abc"));
}
#[test]
fn test_take_until_no_null() {
let data = [b'a', b'b', b'c'];
let until_null: Vec<_> = data.into_iter().take_while(|b| *b != 0).collect();
let string = String::from_utf8(until_null).unwrap();
assert_eq!(string, String::from("abc"));
}
#[test]
fn test_q32() {
let max = I16F16::from_bits(i32::MAX);
println!("Max {}", max);
let min = I16F16::from_bits(i32::MIN);
println!("Min {}", min);
let sum = max + min;
println!("Sum {}", sum);
let max_f64 = f64::from_fixed(max);
println!("Max f64 {}", max_f64);
let min_f64 = f64::from_fixed(min);
println!("Min f64 {}", min_f64);
let max_f32 = f32::from_fixed(max);
println!("Max f32 {}", max_f32);
let min_f32 = f32::from_fixed(min);
println!("Min f32 {}", min_f32);
}
#[test]
fn test_q64() {
let max = I32F32::from_bits(i64::MAX);
println!("Max {}", max);
let min = I32F32::from_bits(i64::MIN);
println!("Min {}", min);
let sum = max + min;
println!("Sum {}", sum);
let max_f64 = f64::from_fixed(max);
println!("Max f64 {}", max_f64);
let min_f64 = f64::from_fixed(min);
println!("Min f64 {}", min_f64);
let max_f32 = f32::from_fixed(max);
println!("Max f32 {}", max_f32);
let min_f32 = f32::from_fixed(min);
println!("Min f32 {}", min_f32);
}
#[test]
fn test_date() {
let date_str = "230323191804.123";
println!("{}", date_str);
let no_timezone = Utc.datetime_from_str(date_str, DATE_FORMAT).unwrap();
println!("{}", no_timezone);
let roundtrip = no_timezone.format(DATE_FORMAT).to_string();
println!("{}", roundtrip);
}
}