Files
fit-parser/QUICKSTART.md
Thomas Klaehn 2945d90d24 Initial commit
Signed-off-by: Thomas Klaehn <thomas.klaehn@perinet.io>
2026-02-08 07:30:31 +00:00

6.7 KiB

Quick Start Guide

Installation

# Get the module
go get git.blackfinn.de/go/fit-parser/fitparser

# Or clone and use locally
git clone https://git.blackfinn.de/go/fit-parser.git

Quick Test

# Run the example
cd fitparser/example
go run main.go testdata/Activity.fit

# Run tests
cd fitparser
go test -v

Basic Usage

1. Simple File Parsing

package main

import (
    "fmt"
    "log"
    "os"
    "git.blackfinn.de/go/fit-parser/fitparser"
)

func main() {
    // Read the FIT file
    data, err := os.ReadFile("activity.fit")
    if err != nil {
        log.Fatal(err)
    }

    // Create decoder
    decoder, err := fitparser.NewDecoder(data)
    if err != nil {
        log.Fatal(err)
    }

    // Decode messages
    messages, err := decoder.Decode()
    if err != nil {
        log.Printf("Warning: %v", err)
        // Continue anyway - CRC errors are non-fatal
    }

    fmt.Printf("Decoded %d messages\n", len(messages))
}

2. Processing Activity Data

for _, msg := range messages {
    switch msg.Num {
    case fitparser.MesgNumFileId:
        // File information
        if fileType, ok := msg.GetFieldValueUint8(fitparser.FieldFileIdType); ok {
            fmt.Printf("File Type: %s\n", fitparser.GetFileTypeName(fileType))
        }

    case fitparser.MesgNumRecord:
        // GPS and sensor data points
        if timestamp, ok := msg.GetFieldValueUint32(fitparser.FieldRecordTimestamp); ok {
            t := fitparser.ConvertFITTimestamp(timestamp)
            fmt.Printf("Time: %s\n", t)
        }

        if hr, ok := msg.GetFieldValueUint8(fitparser.FieldRecordHeartRate); ok {
            if hr != fitparser.Uint8Invalid {
                fmt.Printf("Heart Rate: %d bpm\n", hr)
            }
        }

    case fitparser.MesgNumSession:
        // Activity summary
        if sport, ok := msg.GetFieldValueUint8(fitparser.FieldSessionSport); ok {
            fmt.Printf("Sport: %s\n", fitparser.GetSportName(sport))
        }

        if totalTime, ok := msg.GetFieldValueUint32(fitparser.FieldSessionTotalTimerTime); ok {
            minutes := float64(totalTime) / 1000.0 / 60.0
            fmt.Printf("Duration: %.2f minutes\n", minutes)
        }
    }
}

3. GPS Coordinates

for _, msg := range messages {
    if msg.Num == fitparser.MesgNumRecord {
        // Get latitude
        if lat, ok := msg.Fields[fitparser.FieldRecordPositionLat]; ok {
            if latInt, ok := lat.(int32); ok && latInt != fitparser.Sint32Invalid {
                latDeg := fitparser.ConvertSemicirclesToDegrees(latInt)

                // Get longitude
                if lon, ok := msg.Fields[fitparser.FieldRecordPositionLong]; ok {
                    if lonInt, ok := lon.(int32); ok && lonInt != fitparser.Sint32Invalid {
                        lonDeg := fitparser.ConvertSemicirclesToDegrees(lonInt)
                        fmt.Printf("Position: %.6f, %.6f\n", latDeg, lonDeg)
                    }
                }
            }
        }
    }
}

Running the Example

This repository includes a complete example application:

# From the example directory
cd example
go run main.go testdata/Activity.fit
go run main.go testdata/Settings.fit
go run main.go testdata/RealWorld_Cycling.fit

# Or from the repository root
go run example/main.go example/testdata/Activity.fit

Testing

Run the test suite:

cd fitparser
go test -v

Run benchmarks:

cd fitparser
go test -bench=.

Common Message Types

  • FileId (0) - File metadata (type, manufacturer, product, serial number)
  • Record (20) - Data points (GPS, heart rate, power, cadence, etc.)
  • Lap (19) - Lap summaries
  • Session (18) - Activity session summaries
  • Activity (34) - Overall activity summaries
  • Event (21) - Events (start, stop, lap button, etc.)
  • DeviceInfo (23) - Device information

Field Access Patterns

Type-Safe Accessors

// For common types
uint8Val, ok := msg.GetFieldValueUint8(fieldNum)
uint16Val, ok := msg.GetFieldValueUint16(fieldNum)
uint32Val, ok := msg.GetFieldValueUint32(fieldNum)
stringVal, ok := msg.GetFieldValueString(fieldNum)

Direct Field Access

// Access any field by number
if value, ok := msg.Fields[fieldNum]; ok {
    // Type assert as needed
    switch v := value.(type) {
    case uint8:
        fmt.Printf("Uint8: %d\n", v)
    case uint32:
        fmt.Printf("Uint32: %d\n", v)
    case int32:
        fmt.Printf("Sint32: %d\n", v)
    case string:
        fmt.Printf("String: %s\n", v)
    case []uint8:
        fmt.Printf("Byte array: %v\n", v)
    }
}

Performance Tips

  1. Disable CRC for faster parsing (if you trust your files):

    decoder.EnableCRCCheck(false)
    
  2. Filter messages early if you only need specific types:

    for _, msg := range messages {
        if msg.Num != fitparser.MesgNumRecord {
            continue // Skip non-record messages
        }
        // Process only records
    }
    
  3. Use type-safe accessors when possible - they're optimized

Handling Invalid Values

FIT files use special "invalid" values to indicate missing data:

// Always check for invalid values
if hr, ok := msg.GetFieldValueUint8(fitparser.FieldRecordHeartRate); ok {
    if hr != fitparser.Uint8Invalid {
        fmt.Printf("Heart Rate: %d bpm\n", hr)
    } else {
        fmt.Println("Heart Rate: not available")
    }
}

Invalid value constants:

  • Uint8Invalid = 0xFF
  • Uint16Invalid = 0xFFFF
  • Uint32Invalid = 0xFFFFFFFF
  • Sint8Invalid = 0x7F
  • Sint16Invalid = 0x7FFF
  • Sint32Invalid = 0x7FFFFFFF

Next Steps

  • Read the full README.md for comprehensive documentation
  • Explore the example/main.go for a complete working example
  • Check the fitparser/ directory for implementation details
  • Try parsing your own FIT files!

Troubleshooting

CRC Validation Failures: If CRC validation fails, the file may be corrupted. CRC checking is enabled by default and validates data integrity. You can disable it with decoder.EnableCRCCheck(false) if you trust the file source.

Invalid Field Values: Always check for invalid values using the provided constants (e.g., Uint8Invalid, Uint32Invalid). These indicate missing or unavailable data in the FIT file.

Unknown Message Types: Some proprietary message types may not have names in the profile. They'll show as "Unknown" but can still be accessed by their message number.