254 lines
6.7 KiB
Markdown
254 lines
6.7 KiB
Markdown
# Quick Start Guide
|
|
|
|
## Installation
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```go
|
|
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
|
|
|
|
```go
|
|
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
|
|
|
|
```go
|
|
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:
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
```bash
|
|
cd fitparser
|
|
go test -v
|
|
```
|
|
|
|
Run benchmarks:
|
|
|
|
```bash
|
|
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
|
|
|
|
```go
|
|
// 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
|
|
|
|
```go
|
|
// 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):
|
|
```go
|
|
decoder.EnableCRCCheck(false)
|
|
```
|
|
|
|
2. **Filter messages early** if you only need specific types:
|
|
```go
|
|
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:
|
|
|
|
```go
|
|
// 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](README.md) for comprehensive documentation
|
|
- Explore the [example/main.go](example/main.go) for a complete working example
|
|
- Check the [fitparser/](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.
|