Reading definition headers

main
Jacob Windle 2023-04-25 08:53:20 -04:00
parent 32b806200d
commit 79c21af5dd
2 changed files with 93 additions and 9 deletions

View File

@ -1,6 +1,6 @@
module bytes
export byte_vec_to_int, byte_vec_to_string, bit_at, bit_vector
export byte_vec_to_int, byte_vec_to_string, bit_at, bit_vector, sum_bits
"""
read_data_size(size::Vector{UInt8})::Real
@ -53,5 +53,16 @@ function bit_vector(byte_vector::Vector{UInt8}; little_endian=false)::Vector{UIn
reduce(vcat, vecs)
end
"""
sum_bits(bit_vector::Vector{UInt8})::Int64
Sum the bits found in bit_vector, return a value in Int64, Calculate the value of
each bit along the way.
"""
function sum_bits(bit_vector::Vector{UInt8})::Int64
rev = reverse(bit_vector)
sum([rev[i] * (2 ^ (i - 1)) for i eachindex(rev)])
end
end
# byte_vec_to_string(UInt8.(collect("ABC")))

View File

@ -1,8 +1,11 @@
module file
export FITFile, read_file
import Base.show
export FITFile, read_file, DataRecord
include("bytes.jl"); using .bytes
include("mesg_num.jl"); using .mesg_num
HEADER_SZ = 14
@ -16,9 +19,34 @@ struct FITHeader
crc::AbstractVector{<:Real}
end
mutable struct DataRecord
"""
RecordHeader
Contains the header byte for a record row
"""
mutable struct RecordHeader
definition::Bool
data::Bool
local_mesg_type::Int64
end
mutable struct DefinitionBody
architecture::AbstractString
global_mesg_type::AbstractString # Read from mesg_num.jl using the bytes in this field.
num_fields::UInt8
end
mutable struct DataBody
end
"""
DataRecord
Represents a DataRecord within the Garmin file.
"""
mutable struct DataRecord
hdr::Union{RecordHeader, Nothing}
body::Union{DefinitionBody, DataBody}
end
mutable struct FITFileReader
@ -31,7 +59,8 @@ end
blank_file(fp::AbstractString) = FITFileReader(fp, nothing, 0, nothing, nothing)
blank_row() = DataRecord(false, false)
blank_row() = DataRecord(nothing)
blank_header_byte() = RecordHeader(false, false, 0)
"""
open_fit_file!(f::FITFileReader)
@ -81,15 +110,42 @@ function read_header!(f::FITFileReader)
end
"""
decode_header(v::Vector{UInt8})
decode_header(v::Vector{UInt8})::RecordHeader
Decode the header byte, determine if the record is a definition, data, or timestamp.
"""
function decode_header(v::Vector{UInt8})::DataRecord
record = blank_row()
function decode_header(v::Vector{UInt8})::RecordHeader
record = blank_header_byte()
record.definition = v[7] === 0x01
record.data = !record.definition
record.local_mesg_type = sum_bits(v[end-3:end])
record
end
"""
read_record_content!(f::FITFileReader, hdr::RecordHeader)::Union{DefinitionBody, DataBody}
Using f, read the appropriate message body given the hdr (which tells us if this is def or data)
"""
function read_record_content!(f::FITFileReader, hdr::RecordHeader)::Union{DefinitionBody, DataBody}
# Read the definition record, return the body
if hdr.definition
# Read the fixed 5 bytes first.
def_header_bytes = read_bytes!(f, 5)
def_header_fields = read_bytes!(f, def_header_bytes[end] * 3)
# See if there is developer data
def_header_developer_flag = read_bytes!(f, 1)
if def_header_developer_flag[1] > 0x00
def_header_developer_fields = read_bytes!(f, def_header_developer_flag * 3)
end
DefinitionBody(
def_header_bytes[2] == 0 ? "littleendian" : "bigendian",
get_mesg_num_string(byte_vec_to_int(def_header_bytes[3:4])),
def_header_bytes[end]
)
end
end
"""
@ -104,7 +160,11 @@ function read_records!(f::FITFileReader)
# Read header byte - decode
header_bits = bit_vector(read_bytes!(f, 1); little_endian=true)
record = decode_header(header_bits)
header = decode_header(header_bits)
# Read the record content
record = DataRecord(header, read_record_content!(f, header))
# Push record to the body.
if isnothing(f.body)
@ -132,4 +192,17 @@ function read_file(filepath::AbstractString)::FITFileReader
f
end
function show(io::IO, hdr::RecordHeader)
type = hdr.definition ? "Definition" : "Data"
println(io, "Header($(type), local_message_type=$(hdr.local_mesg_type))")
end
function show(io::IO, ffr::FITFileReader)
println(io, "FITFileReader($(ffr.filepath), bytes_read=$(ffr.bytes_read), header=$(ffr.header), body=$(ffr.body))")
end
function show(io::IO, defbody::DefinitionBody)
println(io, "DefinitionBody(arch=$(defbody.architecture), global_type=$(defbody.global_mesg_type), num_fields=$(defbody.num_fields))")
end
end