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

283 lines
9.1 KiB
Go
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package main
import (
"fmt"
"log"
"os"
"strings"
"git.blackfinn.de/go/fit-parser/fitparser"
)
func main() {
if len(os.Args) < 2 {
fmt.Println("Usage: go run main.go <fit-file>")
fmt.Println("Example: go run main.go testdata/Activity.fit")
fmt.Println()
fmt.Println("Available test files:")
fmt.Println(" testdata/Activity.fit - Full activity with GPS and sensors")
fmt.Println(" testdata/Settings.fit - Device settings file")
fmt.Println(" testdata/MonitoringFile.fit - Daily monitoring data")
fmt.Println(" testdata/WorkoutIndividualSteps.fit - Workout definition")
fmt.Println(" testdata/RealWorld_Cycling.fit - Real-world cycling activity")
os.Exit(1)
}
filePath := os.Args[1]
// Read FIT file
data, err := os.ReadFile(filePath)
if err != nil {
log.Fatalf("Error reading file: %v", err)
}
fmt.Printf("File: %s (%d bytes)\n", filePath, len(data))
fmt.Println(strings.Repeat("=", 60))
// Check if it's a valid FIT file
if !fitparser.IsFIT(data) {
log.Fatal("Not a valid FIT file")
}
// Create decoder
decoder, err := fitparser.NewDecoder(data)
if err != nil {
log.Fatalf("Error creating decoder: %v", err)
}
// Optionally disable CRC checking for faster parsing
// decoder.EnableCRCCheck(false)
// Get header info
header := decoder.GetHeader()
if header != nil {
fmt.Println("\nHeader Information:")
fmt.Printf(" %s\n", header)
} else {
// Parse header to get info
header, _ := fitparser.ParseHeader(data)
fmt.Println("\nHeader Information:")
fmt.Printf(" %s\n", header)
}
// Decode all messages
messages, err := decoder.Decode()
if err != nil {
// CRC errors are common, continue anyway
fmt.Printf("Warning: %v\n", err)
fmt.Println("Continuing with decoded messages...")
}
if len(messages) == 0 {
fmt.Println("No messages found in file")
return
}
fmt.Printf("\nDecoded %d messages\n", len(messages))
// Count message types
msgCounts := make(map[uint16]int)
for _, msg := range messages {
msgCounts[msg.Num]++
}
fmt.Println("\nMessage Type Summary:")
for mesgNum, count := range msgCounts {
fmt.Printf(" %-20s: %5d messages\n", fitparser.GetMessageName(mesgNum), count)
}
// Process key messages
fmt.Println(strings.Repeat("=", 60))
fmt.Println("\nFile Details:")
for _, msg := range messages {
switch msg.Num {
case fitparser.MesgNumFileId:
printFileId(msg)
case fitparser.MesgNumActivity:
printActivity(msg)
case fitparser.MesgNumSession:
printSession(msg)
case fitparser.MesgNumLap:
printLap(msg)
}
}
// Print sample records
printSampleRecords(messages)
}
func printFileId(msg *fitparser.Message) {
fmt.Println("\n📄 File ID:")
if fileType, ok := msg.GetFieldValueUint8(fitparser.FieldFileIdType); ok {
fmt.Printf(" Type: %s\n", fitparser.GetFileTypeName(fileType))
}
if manufacturer, ok := msg.GetFieldValueUint16(fitparser.FieldFileIdManufacturer); ok {
fmt.Printf(" Manufacturer: %d\n", manufacturer)
}
if product, ok := msg.GetFieldValueUint16(fitparser.FieldFileIdProduct); ok {
fmt.Printf(" Product: %d\n", product)
}
if serial, ok := msg.GetFieldValueUint32(fitparser.FieldFileIdSerialNumber); ok && serial != fitparser.Uint32Invalid {
fmt.Printf(" Serial Number: %d\n", serial)
}
if timeCreated, ok := msg.GetFieldValueUint32(fitparser.FieldFileIdTimeCreated); ok {
t := fitparser.ConvertFITTimestamp(timeCreated)
fmt.Printf(" Created: %s\n", t.Format("2006-01-02 15:04:05 MST"))
}
}
func printActivity(msg *fitparser.Message) {
fmt.Println("\n🏃 Activity Summary:")
if timestamp, ok := msg.GetFieldValueUint32(fitparser.FieldActivityTimestamp); ok {
t := fitparser.ConvertFITTimestamp(timestamp)
fmt.Printf(" Timestamp: %s\n", t.Format("2006-01-02 15:04:05 MST"))
}
if totalTime, ok := msg.GetFieldValueUint32(fitparser.FieldActivityTotalTimerTime); ok {
minutes := float64(totalTime) / 1000.0 / 60.0
fmt.Printf(" Total Time: %.2f minutes\n", minutes)
}
if numSessions, ok := msg.GetFieldValueUint16(fitparser.FieldActivityNumSessions); ok {
fmt.Printf(" Number of Sessions: %d\n", numSessions)
}
}
func printSession(msg *fitparser.Message) {
fmt.Println("\n📊 Session 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(" Total Time: %.2f minutes\n", minutes)
}
if totalDist, ok := msg.GetFieldValueUint32(fitparser.FieldSessionTotalDistance); ok {
km := float64(totalDist) / 100000.0
fmt.Printf(" Total Distance: %.2f km\n", km)
}
if avgSpeed, ok := msg.GetFieldValueUint16(fitparser.FieldSessionAvgSpeed); ok && avgSpeed != fitparser.Uint16Invalid {
speed := float64(avgSpeed) / 1000.0
fmt.Printf(" Average Speed: %.2f m/s (%.2f km/h)\n", speed, speed*3.6)
}
if maxSpeed, ok := msg.GetFieldValueUint16(fitparser.FieldSessionMaxSpeed); ok && maxSpeed != fitparser.Uint16Invalid {
speed := float64(maxSpeed) / 1000.0
fmt.Printf(" Max Speed: %.2f m/s (%.2f km/h)\n", speed, speed*3.6)
}
if avgHR, ok := msg.GetFieldValueUint8(fitparser.FieldSessionAvgHeartRate); ok && avgHR != fitparser.Uint8Invalid {
fmt.Printf(" Average Heart Rate: %d bpm\n", avgHR)
}
if maxHR, ok := msg.GetFieldValueUint8(fitparser.FieldSessionMaxHeartRate); ok && maxHR != fitparser.Uint8Invalid {
fmt.Printf(" Max Heart Rate: %d bpm\n", maxHR)
}
if totalCalories, ok := msg.GetFieldValueUint16(fitparser.FieldSessionTotalCalories); ok {
fmt.Printf(" Total Calories: %d kcal\n", totalCalories)
}
if avgPower, ok := msg.GetFieldValueUint16(fitparser.FieldSessionAvgPower); ok && avgPower != fitparser.Uint16Invalid {
fmt.Printf(" Average Power: %d watts\n", avgPower)
}
if maxPower, ok := msg.GetFieldValueUint16(fitparser.FieldSessionMaxPower); ok && maxPower != fitparser.Uint16Invalid {
fmt.Printf(" Max Power: %d watts\n", maxPower)
}
}
func printLap(msg *fitparser.Message) {
fmt.Println("\n⏱ Lap:")
if timestamp, ok := msg.GetFieldValueUint32(fitparser.FieldLapTimestamp); ok {
t := fitparser.ConvertFITTimestamp(timestamp)
fmt.Printf(" Timestamp: %s\n", t.Format("15:04:05"))
}
if totalTime, ok := msg.GetFieldValueUint32(fitparser.FieldLapTotalTimerTime); ok {
seconds := float64(totalTime) / 1000.0
fmt.Printf(" Time: %.2f seconds\n", seconds)
}
if totalDist, ok := msg.GetFieldValueUint32(fitparser.FieldLapTotalDistance); ok {
meters := float64(totalDist) / 100.0
fmt.Printf(" Distance: %.2f meters\n", meters)
}
if avgHR, ok := msg.GetFieldValueUint8(fitparser.FieldLapAvgHeartRate); ok && avgHR != fitparser.Uint8Invalid {
fmt.Printf(" Avg HR: %d bpm\n", avgHR)
}
}
func printSampleRecords(messages []*fitparser.Message) {
fmt.Println(strings.Repeat("=", 60))
fmt.Println("\nSample Records (first 5):")
count := 0
for _, msg := range messages {
if msg.Num == fitparser.MesgNumRecord && count < 5 {
count++
fmt.Printf("\n📍 Record #%d:\n", count)
if timestamp, ok := msg.GetFieldValueUint32(fitparser.FieldRecordTimestamp); ok {
t := fitparser.ConvertFITTimestamp(timestamp)
fmt.Printf(" Time: %s\n", t.Format("15:04:05"))
}
// Position
if lat, ok := msg.Fields[fitparser.FieldRecordPositionLat]; ok {
if latInt, ok := lat.(int32); ok && latInt != fitparser.Sint32Invalid {
latDeg := fitparser.ConvertSemicirclesToDegrees(latInt)
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)
}
}
}
}
if altitude, ok := msg.GetFieldValueUint16(fitparser.FieldRecordAltitude); ok && altitude != fitparser.Uint16Invalid {
alt := float64(altitude)/5.0 - 500.0
fmt.Printf(" Altitude: %.1f m\n", alt)
}
if hr, ok := msg.GetFieldValueUint8(fitparser.FieldRecordHeartRate); ok && hr != fitparser.Uint8Invalid {
fmt.Printf(" Heart Rate: %d bpm\n", hr)
}
if cadence, ok := msg.GetFieldValueUint8(fitparser.FieldRecordCadence); ok && cadence != fitparser.Uint8Invalid {
fmt.Printf(" Cadence: %d rpm\n", cadence)
}
if speed, ok := msg.GetFieldValueUint16(fitparser.FieldRecordSpeed); ok && speed != fitparser.Uint16Invalid {
spd := float64(speed) / 1000.0
fmt.Printf(" Speed: %.2f m/s (%.2f km/h)\n", spd, spd*3.6)
}
if power, ok := msg.GetFieldValueUint16(fitparser.FieldRecordPower); ok && power != fitparser.Uint16Invalid {
fmt.Printf(" Power: %d watts\n", power)
}
if temp, ok := msg.GetFieldValueUint8(fitparser.FieldRecordTemperature); ok && temp != fitparser.Sint8Invalid {
fmt.Printf(" Temperature: %d °C\n", int(temp))
}
}
}
}