diff --git a/src/bytes.jl b/src/bytes.jl index cbb4b00..485238a 100644 --- a/src/bytes.jl +++ b/src/bytes.jl @@ -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"))) diff --git a/src/file.jl b/src/file.jl index cbf583a..f5b2cd3 100644 --- a/src/file.jl +++ b/src/file.jl @@ -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 \ No newline at end of file