Files
go-garminconnect/internal/fit/decoder.go
2025-08-26 19:33:02 -07:00

112 lines
2.2 KiB
Go

package fit
import (
"encoding/binary"
"errors"
"io"
"os"
)
const (
headerSize = 12
protocolMajor = 2
)
// FileHeader represents the header of a FIT file
type FileHeader struct {
Size uint8
Protocol uint8
Profile [4]byte
DataSize uint32
Signature [4]byte
}
// Activity represents activity data from a FIT file
type Activity struct {
Type string
StartTime int64
TotalDistance float64
Duration float64
}
// Decoder parses FIT files
type Decoder struct {
r io.Reader
}
// NewDecoder creates a new FIT decoder
func NewDecoder(r io.Reader) *Decoder {
return &Decoder{r: r}
}
// Parse decodes the FIT file and returns the activity data
func (d *Decoder) Parse() (*Activity, error) {
var header FileHeader
if err := binary.Read(d.r, binary.LittleEndian, &header); err != nil {
return nil, err
}
// Validate header
if header.Protocol != protocolMajor {
return nil, errors.New("unsupported FIT protocol version")
}
// For simplicity, we'll just extract basic activity data
activity := &Activity{}
// Skip to activity record (simplified for example)
// In a real implementation, we would parse the file structure properly
for {
var recordHeader uint8
if err := binary.Read(d.r, binary.LittleEndian, &recordHeader); err == io.EOF {
break
} else if err != nil {
return nil, err
}
if recordHeader == 0x21 { // Activity record header (example value)
var record struct {
Type uint8
StartTime int64
TotalDistance float32
Duration uint32
}
if err := binary.Read(d.r, binary.LittleEndian, &record); err != nil {
return nil, err
}
activity.Type = activityType(record.Type)
activity.StartTime = record.StartTime
activity.TotalDistance = float64(record.TotalDistance)
activity.Duration = float64(record.Duration)
break
}
}
return activity, nil
}
func activityType(t uint8) string {
switch t {
case 1:
return "Running"
case 2:
return "Cycling"
case 3:
return "Swimming"
default:
return "Unknown"
}
}
// ReadFile reads and parses a FIT file
func ReadFile(path string) (*Activity, error) {
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()
return NewDecoder(file).Parse()
}