Initial commit

Signed-off-by: Thomas Klaehn <thomas.klaehn@perinet.io>
This commit is contained in:
Thomas Klaehn
2026-02-08 07:28:32 +00:00
commit 2945d90d24
25 changed files with 3177 additions and 0 deletions

182
fitparser/types.go Normal file
View 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
}