110
fitparser/header.go
Normal file
110
fitparser/header.go
Normal file
@@ -0,0 +1,110 @@
|
||||
package fitparser
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Header represents a FIT file header
|
||||
type Header struct {
|
||||
Size uint8 // Header size (12 or 14 bytes)
|
||||
ProtocolVersion uint8 // Protocol version
|
||||
ProfileVersion uint16 // Profile version
|
||||
DataSize uint32 // Size of data records in bytes
|
||||
DataType [4]byte // ".FIT" signature
|
||||
CRC uint16 // CRC of header (only present if Size == 14)
|
||||
}
|
||||
|
||||
// ParseHeader parses a FIT file header from the given data
|
||||
func ParseHeader(data []byte) (*Header, error) {
|
||||
if len(data) < HeaderMinSize {
|
||||
return nil, fmt.Errorf("header too small: got %d bytes, need at least %d", len(data), HeaderMinSize)
|
||||
}
|
||||
|
||||
h := &Header{
|
||||
Size: data[0],
|
||||
ProtocolVersion: data[1],
|
||||
DataType: [4]byte{data[8], data[9], data[10], data[11]},
|
||||
}
|
||||
|
||||
// Validate header size
|
||||
if h.Size != HeaderWithCRCSize && h.Size != HeaderWithoutCRCSize {
|
||||
return nil, fmt.Errorf("invalid header size: %d (expected %d or %d)",
|
||||
h.Size, HeaderWithoutCRCSize, HeaderWithCRCSize)
|
||||
}
|
||||
|
||||
// Ensure we have enough data
|
||||
if len(data) < int(h.Size) {
|
||||
return nil, fmt.Errorf("insufficient data for header: got %d bytes, need %d", len(data), h.Size)
|
||||
}
|
||||
|
||||
// Parse profile version (little endian)
|
||||
h.ProfileVersion = binary.LittleEndian.Uint16(data[2:4])
|
||||
|
||||
// Parse data size (little endian)
|
||||
h.DataSize = binary.LittleEndian.Uint32(data[4:8])
|
||||
|
||||
// Validate signature
|
||||
if h.DataType != FITSignature {
|
||||
return nil, fmt.Errorf("invalid FIT signature: got %s, expected .FIT", string(h.DataType[:]))
|
||||
}
|
||||
|
||||
// Parse CRC if present
|
||||
if h.Size == HeaderWithCRCSize {
|
||||
h.CRC = binary.LittleEndian.Uint16(data[12:14])
|
||||
|
||||
// Validate header CRC
|
||||
calculatedCRC := CalculateCRC(data[0:12])
|
||||
if calculatedCRC != h.CRC {
|
||||
return nil, fmt.Errorf("header CRC mismatch: got 0x%04X, expected 0x%04X", h.CRC, calculatedCRC)
|
||||
}
|
||||
}
|
||||
|
||||
return h, nil
|
||||
}
|
||||
|
||||
// Validate performs basic validation on the header
|
||||
func (h *Header) Validate() error {
|
||||
if h.Size != HeaderWithCRCSize && h.Size != HeaderWithoutCRCSize {
|
||||
return fmt.Errorf("invalid header size: %d", h.Size)
|
||||
}
|
||||
|
||||
if h.DataType != FITSignature {
|
||||
return fmt.Errorf("invalid FIT signature: %s", string(h.DataType[:]))
|
||||
}
|
||||
|
||||
protocolMajor := h.ProtocolVersion >> ProtocolVersionMajorShift
|
||||
if protocolMajor < 1 || protocolMajor > 2 {
|
||||
return fmt.Errorf("unsupported protocol version: %d.%d",
|
||||
protocolMajor, h.ProtocolVersion&ProtocolVersionMinorMask)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ProtocolVersionMajor returns the major version of the protocol
|
||||
func (h *Header) ProtocolVersionMajor() uint8 {
|
||||
return h.ProtocolVersion >> ProtocolVersionMajorShift
|
||||
}
|
||||
|
||||
// ProtocolVersionMinor returns the minor version of the protocol
|
||||
func (h *Header) ProtocolVersionMinor() uint8 {
|
||||
return h.ProtocolVersion & ProtocolVersionMinorMask
|
||||
}
|
||||
|
||||
// IsFIT checks if the given data starts with a valid FIT file signature
|
||||
func IsFIT(data []byte) bool {
|
||||
if len(data) < HeaderMinSize {
|
||||
return false
|
||||
}
|
||||
|
||||
signature := [4]byte{data[8], data[9], data[10], data[11]}
|
||||
return bytes.Equal(signature[:], FITSignature[:])
|
||||
}
|
||||
|
||||
// String returns a string representation of the header
|
||||
func (h *Header) String() string {
|
||||
return fmt.Sprintf("FIT Header: Protocol v%d.%d, Profile v%d, DataSize=%d bytes, HeaderSize=%d bytes",
|
||||
h.ProtocolVersionMajor(), h.ProtocolVersionMinor(), h.ProfileVersion, h.DataSize, h.Size)
|
||||
}
|
||||
Reference in New Issue
Block a user