182
fitparser/types.go
Normal file
182
fitparser/types.go
Normal file
@@ -0,0 +1,182 @@
|
||||
// Package fitparser provides functionality to decode FIT (Flexible and Interoperable Data Transfer) files.
|
||||
// FIT is a binary file format used by Garmin and other fitness device manufacturers.
|
||||
package fitparser
|
||||
|
||||
import "time"
|
||||
|
||||
// Protocol and Profile versions
|
||||
const (
|
||||
ProtocolVersionMajorShift = 4
|
||||
ProtocolVersionMajorMask = 0xF0
|
||||
ProtocolVersionMinorMask = 0x0F
|
||||
ProtocolVersion10 = 0x10
|
||||
ProtocolVersion20 = 0x20
|
||||
ProfileVersionMajor = 21
|
||||
ProfileVersionMinor = 188
|
||||
)
|
||||
|
||||
// Base type definitions
|
||||
const (
|
||||
BaseTypeEnum = 0x00
|
||||
BaseTypeSint8 = 0x01
|
||||
BaseTypeUint8 = 0x02
|
||||
BaseTypeSint16 = 0x83
|
||||
BaseTypeUint16 = 0x84
|
||||
BaseTypeSint32 = 0x85
|
||||
BaseTypeUint32 = 0x86
|
||||
BaseTypeString = 0x07
|
||||
BaseTypeFloat32 = 0x88
|
||||
BaseTypeFloat64 = 0x89
|
||||
BaseTypeUint8z = 0x0A
|
||||
BaseTypeUint16z = 0x8B
|
||||
BaseTypeUint32z = 0x8C
|
||||
BaseTypeByte = 0x0D
|
||||
BaseTypeSint64 = 0x8E
|
||||
BaseTypeUint64 = 0x8F
|
||||
BaseTypeUint64z = 0x90
|
||||
)
|
||||
|
||||
// Base type flags
|
||||
const (
|
||||
BaseTypeEndianFlag = 0x80
|
||||
BaseTypeNumMask = 0x1F
|
||||
)
|
||||
|
||||
// Invalid values for each type
|
||||
const (
|
||||
EnumInvalid = 0xFF
|
||||
Sint8Invalid = 0x7F
|
||||
Uint8Invalid = 0xFF
|
||||
Sint16Invalid = 0x7FFF
|
||||
Uint16Invalid = 0xFFFF
|
||||
Sint32Invalid = 0x7FFFFFFF
|
||||
Uint32Invalid = 0xFFFFFFFF
|
||||
StringInvalid = 0x00
|
||||
Float32Invalid = 0xFFFFFFFF
|
||||
Float64Invalid = 0xFFFFFFFFFFFFFFFF
|
||||
Uint8zInvalid = 0x00
|
||||
Uint16zInvalid = 0x0000
|
||||
Uint32zInvalid = 0x00000000
|
||||
ByteInvalid = 0xFF
|
||||
Sint64Invalid = 0x7FFFFFFFFFFFFFFF
|
||||
Uint64Invalid = 0xFFFFFFFFFFFFFFFF
|
||||
Uint64zInvalid = 0x0000000000000000
|
||||
)
|
||||
|
||||
// Header sizes
|
||||
const (
|
||||
HeaderWithCRCSize = 14
|
||||
HeaderWithoutCRCSize = 12
|
||||
HeaderMinSize = 12
|
||||
CRCSize = 2
|
||||
)
|
||||
|
||||
// Record header bits
|
||||
const (
|
||||
RecordHeaderMask = 0x80
|
||||
RecordHeaderNormal = 0x00
|
||||
RecordHeaderCompressedMask = 0x80
|
||||
RecordHeaderLocalMesgNumMask = 0x0F
|
||||
RecordHeaderTimeMask = 0x1F
|
||||
)
|
||||
|
||||
// Message definition bits
|
||||
const (
|
||||
MesgDefinitionMask = 0x40
|
||||
MesgHeaderMask = 0xF0
|
||||
LocalMesgNum = 0x0F
|
||||
MesgDefinitionReserved = 0x00
|
||||
DevDataMask = 0x20
|
||||
)
|
||||
|
||||
// FIT file signature
|
||||
var FITSignature = [4]byte{'.', 'F', 'I', 'T'}
|
||||
|
||||
// FIT Epoch - Number of seconds between Unix Epoch and FIT Epoch (Dec 31, 1989 00:00:00 UTC)
|
||||
const FITEpochSeconds = 631065600
|
||||
|
||||
// ConvertFITTimestamp converts a FIT timestamp to a Go time.Time
|
||||
func ConvertFITTimestamp(fitTime uint32) time.Time {
|
||||
if fitTime == Uint32Invalid {
|
||||
return time.Time{}
|
||||
}
|
||||
return time.Unix(int64(fitTime)+FITEpochSeconds, 0).UTC()
|
||||
}
|
||||
|
||||
// BaseTypeSize returns the size in bytes of the given base type
|
||||
func BaseTypeSize(baseType byte) int {
|
||||
switch baseType & BaseTypeNumMask {
|
||||
case BaseTypeEnum, BaseTypeSint8, BaseTypeUint8, BaseTypeByte, BaseTypeUint8z:
|
||||
return 1
|
||||
case BaseTypeSint16, BaseTypeUint16, BaseTypeUint16z:
|
||||
return 2
|
||||
case BaseTypeSint32, BaseTypeUint32, BaseTypeUint32z, BaseTypeFloat32:
|
||||
return 4
|
||||
case BaseTypeSint64, BaseTypeUint64, BaseTypeUint64z, BaseTypeFloat64:
|
||||
return 8
|
||||
case BaseTypeString:
|
||||
return 1 // Variable length, but each character is 1 byte
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// IsEndianType returns true if the base type requires endian conversion
|
||||
func IsEndianType(baseType byte) bool {
|
||||
return (baseType & BaseTypeEndianFlag) != 0
|
||||
}
|
||||
|
||||
// IsInvalidValue checks if a value is invalid for its type
|
||||
func IsInvalidValue(baseType byte, value interface{}) bool {
|
||||
switch baseType & BaseTypeNumMask {
|
||||
case BaseTypeEnum, BaseTypeUint8:
|
||||
if v, ok := value.(uint8); ok {
|
||||
return v == Uint8Invalid
|
||||
}
|
||||
case BaseTypeSint8:
|
||||
if v, ok := value.(int8); ok {
|
||||
return v == Sint8Invalid
|
||||
}
|
||||
case BaseTypeUint16:
|
||||
if v, ok := value.(uint16); ok {
|
||||
return v == Uint16Invalid
|
||||
}
|
||||
case BaseTypeSint16:
|
||||
if v, ok := value.(int16); ok {
|
||||
return v == Sint16Invalid
|
||||
}
|
||||
case BaseTypeUint32:
|
||||
if v, ok := value.(uint32); ok {
|
||||
return v == Uint32Invalid
|
||||
}
|
||||
case BaseTypeSint32:
|
||||
if v, ok := value.(int32); ok {
|
||||
return v == Sint32Invalid
|
||||
}
|
||||
case BaseTypeUint64:
|
||||
if v, ok := value.(uint64); ok {
|
||||
return v == Uint64Invalid
|
||||
}
|
||||
case BaseTypeSint64:
|
||||
if v, ok := value.(int64); ok {
|
||||
return v == Sint64Invalid
|
||||
}
|
||||
case BaseTypeUint8z:
|
||||
if v, ok := value.(uint8); ok {
|
||||
return v == Uint8zInvalid
|
||||
}
|
||||
case BaseTypeUint16z:
|
||||
if v, ok := value.(uint16); ok {
|
||||
return v == Uint16zInvalid
|
||||
}
|
||||
case BaseTypeUint32z:
|
||||
if v, ok := value.(uint32); ok {
|
||||
return v == Uint32zInvalid
|
||||
}
|
||||
case BaseTypeUint64z:
|
||||
if v, ok := value.(uint64); ok {
|
||||
return v == Uint64zInvalid
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user