mod consts;
mod developer_fields;
mod io;
mod value;
use consts::*;
use copyless::VecHelper;
use developer_fields::{DeveloperFieldDefinition, DeveloperFieldDescription};
use fitsdk::{
match_predefined_field_value, match_message_field, match_message_offset, match_message_scale,
match_message_timestamp_field, match_messagetype, FieldType, MessageType,
};
use io::*;
use memmap::{Mmap, MmapOptions};
use std::collections::VecDeque;
use std::io::{Cursor, Read, Seek, SeekFrom};
use std::{fs::File, path::PathBuf};
pub use value::Value;
pub struct Fit {
file_header: FileHeader,
data_len: u64,
buf: Cursor<Mmap>,
queue: VecDeque<(u8, DefinitionRecord)>,
developer_fields: Vec<DeveloperFieldDescription>,
last_timestamp: u32,
}
impl Fit {
pub fn new(path: &PathBuf) -> Self {
let file = File::open(path).unwrap();
let mmap = unsafe { MmapOptions::new().map(&file).unwrap() };
let mut buf = Cursor::new(mmap);
let fh = FileHeader::new(&mut buf);
Self {
data_len: u64::from(&fh.num_record_bytes + 14),
file_header: fh,
buf: buf,
queue: VecDeque::new(),
developer_fields: Vec::new(),
last_timestamp: 0,
}
}
pub fn file_header(&self) -> &FileHeader {
&self.file_header
}
}
impl Iterator for Fit {
type Item = Message;
fn next(&mut self) -> Option<Self::Item> {
loop {
let r = self.buf.seek(SeekFrom::Current(0)).unwrap();
if r > self.data_len {
return None;
}
let h = HeaderByte::new(&mut self.buf);
if h.definition {
let d = DefinitionRecord::new(&mut self.buf, h.dev_fields);
self.queue.push_front((h.local_num, d));
} else {
let definition = match self.queue.iter().find(|x| x.0 == h.local_num) {
None => continue,
Some((_, def)) => def,
};
let message_type = match_messagetype(definition.global_message_number);
let mut dev_fields: Option<Vec<DevDataField>> = None;
let mut values = Vec::with_capacity(definition.field_definitions.len());
for fd in definition.field_definitions.iter() {
if message_type == MessageType::None {
skip_bytes(&mut self.buf, fd.size);
} else if let Some(data) =
read_next_field(fd.base_type, fd.size, definition.endianness, &mut self.buf)
{
values
.alloc()
.init(DataField::new(fd.definition_number, data));
}
}
if message_type == MessageType::FieldDescription {
let d = DeveloperFieldDescription::new(values);
self.developer_fields.push(d);
continue;
}
if message_type == MessageType::None || values.is_empty() {
continue;
}
if let Some(dev_field_defs) = &definition.developer_fields {
let mut temp_dev_fields = Vec::new();
for df in dev_field_defs.iter() {
for e in &self.developer_fields {
if e.developer_data_index == 1 {
if let Some(v) = read_next_field(
e.fit_base_type,
df.size,
definition.endianness,
&mut self.buf,
) {
temp_dev_fields.push(DevDataField::new(
df.developer_data_index,
df.field_number,
v,
));
}
}
}
}
dev_fields = Some(temp_dev_fields);
}
let scales = match_message_scale(message_type);
let offsets = match_message_offset(message_type);
let fields = match_message_field(message_type);
for v in &mut values {
process_value(v, fields, scales, offsets);
}
if let Some(time_offset) = h.compressed_timestamp() {
let mut timestamp = self.last_timestamp
& COMPRESSED_HEADER_LAST_TIMESTAMP_MASK + u32::from(time_offset);
if time_offset
< (self.last_timestamp as u8 & COMPRESSED_HEADER_TIME_OFFSET_MASK)
{
timestamp += COMPRESSED_HEADER_TIME_OFFSET_ROLLOVER
};
if let Some(timestamp_field_number) =
match_message_timestamp_field(message_type)
{
values.alloc().init(DataField::new(
timestamp_field_number,
Value::Time(timestamp + PSEUDO_EPOCH),
));
}
}
values.shrink_to_fit();
return Some(Message {
values: values,
kind: message_type,
dev_values: dev_fields,
});
}
}
}
}
#[allow(clippy::cognitive_complexity)]
fn read_next_field<R>(
base_type: u8,
size: u8,
endianness: Endianness,
map: &mut R,
) -> Option<Value>
where
R: Read + Seek,
{
match base_type {
0 | 13 => {
if size > 1 {
println!("0/13:enum/byte: {}", size);
skip_bytes(map, size);
None
} else {
match read_u8(map) {
0xFF => None,
v => Some(Value::U8(v)),
}
}
}
1 => {
if size > 1 {
println!("1 i8: {}", size);
skip_bytes(map, size);
None
} else {
match read_i8(map) {
0x7F => None,
v => Some(Value::I8(v)),
}
}
}
2 => {
if size > 1 {
let mut buf: Vec<_> = Vec::with_capacity(size.into());
let _ = map.take(size.into()).read_to_end(&mut buf);
buf.retain(|x| *x != 0xFF);
if buf.is_empty() {
None
} else {
Some(Value::ArrU8(buf))
}
} else {
match read_u8(map) {
0xFF => None,
v => Some(Value::U8(v)),
}
}
}
3 => {
let number_of_values = size / 2;
if number_of_values > 1 {
println!("3 i16: {}", size);
skip_bytes(map, size);
None
} else {
let val = read_i16(map, endianness);
if val == 0x7FFF {
None
} else {
Some(Value::I16(val))
}
}
}
4 => {
let number_of_values = size / 2;
if number_of_values > 1 {
let c: Vec<_> = (0..number_of_values)
.filter_map(|_| match read_u16(map, endianness) {
0xFFFF => None,
v => Some(v),
})
.collect();
if c.is_empty() {
None
} else {
Some(Value::ArrU16(c))
}
} else {
let val = read_u16(map, endianness);
if val == 0xFFFF {
None
} else {
Some(Value::U16(val))
}
}
}
5 => {
let number_of_values = size / 4;
if number_of_values > 1 {
println!("5 i32: {}", size);
skip_bytes(map, size);
None
} else {
let val = read_i32(map, endianness);
if val == 0x7F_FFF_FFF {
None
} else {
Some(Value::I32(val))
}
}
}
6 => {
let number_of_values = size / 4;
if number_of_values > 1 {
let c: Vec<_> = (0..number_of_values)
.filter_map(|_| match read_u32(map, endianness) {
0xFFFF_FFFF => None,
v => Some(v),
})
.collect();
if c.is_empty() {
None
} else {
Some(Value::ArrU32(c))
}
} else {
let val = read_u32(map, endianness);
if val == 0xFFFF_FFFF {
None
} else {
Some(Value::U32(val))
}
}
}
7 => {
let mut buf: Vec<_> = Vec::with_capacity(size.into());
let _ = map.take(size.into()).read_to_end(&mut buf);
buf.retain(|b| *b != 0x00);
if let Ok(string) = String::from_utf8(buf) {
Some(Value::String(string))
} else {
None
}
}
8 => {
let number_of_values = size / 4;
if number_of_values > 1 {
println!("8 f32: {}", size);
skip_bytes(map, size);
None
} else {
let uval = read_u32(map, endianness);
if uval == 0xFF_FFF_FFF {
None
} else {
let val = f32::from_bits(uval);
Some(Value::F32(val))
}
}
}
9 => {
let number_of_values = size / 8;
if number_of_values > 1 {
println!("9 f64: {}", size);
skip_bytes(map, size);
None
} else {
let uval = read_u64(map, endianness);
if uval == 0xF_FFF_FFF_FFF_FFF_FFF {
None
} else {
let val = f64::from_bits(uval);
Some(Value::F64(val))
}
}
}
10 => {
if size > 1 {
println!("10:uint8z {}", size);
skip_bytes(map, size);
None
} else {
let val = read_u8(map);
if val == 0x00 {
None
} else {
Some(Value::U8(val))
}
}
}
11 => {
let number_of_values = size / 2;
if number_of_values > 1 {
println!("11 u16: {}", size);
skip_bytes(map, size);
None
} else {
let val = read_u16(map, endianness);
if val == 0x0000 {
None
} else {
Some(Value::U16(val))
}
}
}
12 => {
let number_of_values = size / 4;
if number_of_values > 1 {
println!("12 u32: {}", size);
skip_bytes(map, size);
None
} else {
let val = read_u32(map, endianness);
if val == 0x0000_0000 {
None
} else {
Some(Value::U32(val))
}
}
}
14 => {
let number_of_values = size / 8;
if number_of_values > 1 {
println!("14 i64: {}", size);
skip_bytes(map, size);
None
} else {
let val = read_i64(map, endianness);
if val == 0x7_FFF_FFF_FFF_FFF_FFF {
None
} else {
Some(Value::I64(val))
}
}
}
15 => {
let number_of_values = size / 8;
if number_of_values > 1 {
println!("15 u64: {}", size);
skip_bytes(map, size);
None
} else {
let val = read_u64(map, endianness);
if val == 0xF_FFF_FFF_FFF_FFF_FFF {
None
} else {
Some(Value::U64(val))
}
}
}
16 => {
let number_of_values = size / 8;
if number_of_values > 1 {
println!("16 u64: {}", size);
skip_bytes(map, size);
None
} else {
let val = read_u64(map, endianness);
if val == 0x0_000_000_000_000_000 {
None
} else {
Some(Value::U64(val))
}
}
}
_ => None,
}
}
fn process_value(
v: &mut DataField,
fields: &fitsdk::MatchFieldTypeFn,
scales: &fitsdk::MatchScaleFn,
offsets: &fitsdk::MatchOffsetFn,
) {
match fields(v.field_num) {
FieldType::None => (),
FieldType::Coordinates => {
if let Value::I32(ref inner) = v.value {
let coord = *inner as f32 * COORD_SEMICIRCLES_CALC;
std::mem::replace(&mut v.value, Value::F32(coord));
}
}
FieldType::Timestamp => {
if let Value::U32(ref inner) = v.value {
let date = *inner + PSEUDO_EPOCH;
std::mem::replace(&mut v.value, Value::Time(date));
}
}
FieldType::DateTime => {
if let Value::U32(ref inner) = v.value {
let date = *inner + PSEUDO_EPOCH;
std::mem::replace(&mut v.value, Value::Time(date));
}
}
FieldType::LocalDateTime => {
if let Value::U32(ref inner) = v.value {
let time = *inner + PSEUDO_EPOCH - 3600;
std::mem::replace(&mut v.value, Value::Time(time));
}
}
FieldType::String | FieldType::LocaltimeIntoDay => {}
FieldType::Uint8
| FieldType::Uint8Z
| FieldType::Uint16
| FieldType::Uint16Z
| FieldType::Uint32
| FieldType::Uint32Z
| FieldType::Sint8 => {
if let Some(s) = scales(v.field_num) {
v.value.scale(s);
}
if let Some(o) = offsets(v.field_num) {
v.value.offset(o);
}
}
f => {
if let Value::U8(k) = v.value {
if let Some(t) = match_predefined_field_value(f, usize::from(k)) {
std::mem::replace(&mut v.value, Value::Enum(t));
}
} else if let Value::U16(k) = v.value {
if let Some(t) = match_predefined_field_value(f, usize::from(k)) {
std::mem::replace(&mut v.value, Value::Enum(t));
}
}
}
}
}
#[derive(Clone, Debug)]
pub struct Message {
pub kind: MessageType,
pub values: Vec<DataField>,
pub dev_values: Option<Vec<DevDataField>>,
}
#[derive(Debug)]
pub struct FileHeader {
pub filesize: u8,
pub protocol: u8,
pub profile_version: u16,
num_record_bytes: u32,
fileext: bool,
crc: u16,
}
impl FileHeader {
fn new<R>(map: &mut R) -> Self
where
R: Read,
{
Self {
filesize: read_u8(map),
protocol: read_u8(map),
profile_version: read_u16(map, Endianness::Little),
num_record_bytes: read_u32(map, Endianness::Little),
fileext: {
let buf = arr4(map);
&buf == b".FIT"
},
crc: read_u16(map, Endianness::Little),
}
}
}
#[derive(Debug)]
struct HeaderByte {
compressed_header: bool,
definition: bool,
dev_fields: bool,
local_num: u8,
time_offset: Option<u8>,
}
impl HeaderByte {
fn new<R>(map: &mut R) -> Self
where
R: Read,
{
let b = read_u8(map);
if (b & COMPRESSED_HEADER_MASK) == COMPRESSED_HEADER_MASK {
Self {
compressed_header: true,
definition: false,
dev_fields: false,
local_num: (b & COMPRESSED_HEADER_LOCAL_MESSAGE_NUMBER_MASK) >> 5,
time_offset: Some(b & COMPRESSED_HEADER_TIME_OFFSET_MASK),
}
} else {
Self {
compressed_header: false,
definition: (b & DEFINITION_HEADER_MASK) == DEFINITION_HEADER_MASK,
dev_fields: (b & DEVELOPER_FIELDS_MASK) == DEVELOPER_FIELDS_MASK,
local_num: b & LOCAL_MESSAGE_NUMBER_MASK,
time_offset: None,
}
}
}
fn compressed_timestamp(self) -> Option<u8> {
if self.compressed_header {
return self.time_offset;
} else {
return None;
}
}
}
struct DefinitionRecord {
endianness: Endianness,
global_message_number: u16,
field_definitions: Vec<FieldDefinition>,
developer_fields: Option<Vec<DeveloperFieldDefinition>>,
}
impl DefinitionRecord {
fn new<R>(map: &mut R, dev_fields: bool) -> Self
where
R: Read + Seek,
{
skip_bytes(map, 1);
let mut buffer: Vec<FieldDefinition> = Vec::new();
let endian = match read_u8(map) {
1 => Endianness::Big,
0 => Endianness::Little,
_ => panic!("unexpected endian byte"),
};
let global_message_number = read_u16(map, endian);
let number_of_fields = read_u8(map);
for _ in 0..number_of_fields {
buffer.push(FieldDefinition::new(map));
}
let dev_fields: Option<Vec<DeveloperFieldDefinition>> = if dev_fields {
let number_of_fields = read_u8(map);
Some(
(0..number_of_fields)
.map(|_| DeveloperFieldDefinition::new(map))
.collect(),
)
} else {
None
};
DefinitionRecord {
endianness: endian,
global_message_number,
field_definitions: buffer,
developer_fields: dev_fields,
}
}
}
#[derive(Clone, Debug)]
pub struct DataField {
pub field_num: usize,
pub value: Value,
}
impl DataField {
fn new(fnum: usize, v: Value) -> Self {
Self {
field_num: fnum,
value: v,
}
}
}
#[derive(Clone, Debug)]
pub struct DevDataField {
pub data_index: u8,
pub field_num: u8,
pub value: Value,
}
impl DevDataField {
fn new(ddi: u8, fnum: u8, v: Value) -> Self {
Self {
data_index: ddi,
field_num: fnum,
value: v,
}
}
}
#[derive(Debug, Copy, Clone)]
struct FieldDefinition {
definition_number: usize,
size: u8,
base_type: u8,
}
impl FieldDefinition {
fn new<R>(map: &mut R) -> Self
where
R: Read,
{
let mut buf: [u8; 3] = [0; 3];
let _ = map.read(&mut buf);
Self {
definition_number: buf[0].into(),
size: buf[1],
base_type: buf[2] & FIELD_DEFINITION_BASE_NUMBER,
}
}
}