253
QUICKSTART.md
Normal file
253
QUICKSTART.md
Normal file
@@ -0,0 +1,253 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user