# 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.