Garmin FIT failide töötlemine

Allikas: Tipikate Rattamatkaklubi teabebaas
Jump to navigation Jump to search

Sissejuhatus

Käesolev artikkel keskendub Garmin FIT failidele, mis on pärit Garmini seikluskaameratest. Sama formaati kasutab Garmin andmete talletamiseks ka kõigil muudel oma seadmetel (navigaatorid, tervisespordi abivahendid, ...), millel sensorid küljes on (GPS, kiirendusandur, ...).

Tegemist on väga rikkaliku formaadiga, kuhu on tavaks talletada väga palju erinevaid parameetreid. Samuti on seal palju rõhku pandud andmete ajalisele täpsusele. Erinevalt näiteks GPX formaadist, kus rajapunktid talletatakse sekundi täpsusega, on FIT failis ajaühikuks millisekund.

Teisalt on FIT faili parsimine palju keerulisem kui GPX parsimine. Tegemist on binaarse formaadiga, mis võimaldab talletada võimalikult palju andmeid võimalikult vähese andmemahu kuluga.

Meid huvitab FIT faili parsimine hetkel peamiselt kahel eesmärgil:

  • Saada sealt kätte kaamera rajalogi.
  • Saada kätte kaamera sündmuste ajatemplid - ehk siis millal algas filmimine, millal lõppes ja millal kaamera automaatselt video tükeldas.

Neist esimene on võrdlemisi lihtne ülesanne, teine mitte nii väga. Aga skriptitav lahendus on allpool ära toodud siiski mõlema jaoks.

FIT faile kasutab näiteks Garmin Virb Edit - seal oleva info baasilt näidatakse kaardil filmimise asukohta, samuti on võimalik sensorite info baasilt videosid stabiliseerida. Samuti on võimalik FIT faile avada otse Garmin BaseCamp-is.

Rajalogi eksport

Rajalogi oskab FIT failidest kätte saada näiteks Garmin BaseCamp.

Skriptitavaks lahenduseks sobib aga GPSBabel. Tasub mainida, et koos korrektsete ajatemplitega oskab FIT failidest andmed kätte saada ainult GPSBabel arendusversioon, mitte aga 2017. aasta jaanuarist pärit viimane väljalase 1.5.4. Täpsemalt saab nendest nüanssidest lugeda GPSBabel lehelt.

Konverteerimine näeb välja näiteks nii:

gpsbabel -t -i garmin_fit -f input.fit \
    -x nuketypes,waypoints,routes -x track,start=2000 \
    -o gpx -F out.gpx

Selgituseks veel:

  • Esimene rida impordib FIT faili.
  • Teine rida jätab alles ainult rajalogi ning ainult need punktid, millel on olemas ka ajatempel ning see pärineb vähemalt aastast 2000.
  • Kolmas rada ekspordib GPX faili.

Kui on soov saadud GPX faili kasutada Garmin Virb Edit-is, siis tuleks viimasel reana kasutada järgmist:

-o gpx,gpsver=1.1 -F out.gpx

Nimelt ei saa Garmin Virb Edit hakkama GPX versiooniga 1.0, mida GPSBabel antud juhul aga vaikimisi ekspordiks.

Kaamera sündmuste eksport

Sissejuhatus

Kuigi FIT failidest rajalogi eksportimiseks kõlbab GPSBabel hästi, siis sellega tema tugi ka piirdub - mingeid muid andmeid GPSBabel abil kätte ei saa. Seetõttu tuleb kaamera sündmuste kättesaamiseks kasutada siin peatükis kirjeldatud keerukamat meetodit.

Eelduste paigaldamine

Internetist leitud juhend, mis käesolevaks lahenduseks inspiratsiooni andis, on see.

Järgnevalt on ära toodud aga konkreetne meie jaoks välja töötatud lahendus. Tööle sai see pandud Ubuntu all, nagu ikka. Kõigepealt tuleks paigaldada eeldused:

sudo apt-get install python3-pip
sudo pip3 install --upgrade pip
sudo mkdir -p /opt/fitparse
cd /opt/fitparse
sudo pip3 install -e git+https://github.com/dtcooper/python-fitparse#egg=python-fitparse

Python skript

Järgnevalt sai loodud Python skript, mis võtab parameetrina FIT faili nime ning trükib konsooli välja kõik kaamera sündmused. Iga rida vastab ühele sündmusele ning reas on kaks tühikuga eraldatud tulpa: sündmuse Unix Timestamp (koos komakohtadega) ja sündmuse nimi ("video_start", "video_end", "video_split", "photo_taken" jne). Selleks, et siinne python skript läheks kokku järgmises peatükis toodud wrapper-iga, peaks siinse faili nimi olema print_fit_camera_events.py.

import sys
import calendar
import datetime
import fitparse

# Usage: python3 print_fit_camera_events.py <FIT file name>

def main():
    file=sys.argv[1]
    fitfile = fitparse.FitFile(file,
        data_processor=fitparse.StandardUnitsDataProcessor())
    zero_time=find_zero_timestamp(fitfile)
    print_camera_events(fitfile,zero_time)

def find_zero_timestamp(fitfile):
    required_fields = ['timestamp','timestamp_ms',
                       'system_timestamp','system_timestamp_ms']
    data = find_fields(fitfile,required_fields)
    if len(data) != 1:
        print('Expected exactly one time reference, but found %d' % len(data))
        return 0
    else:
        entry = data[0]
        return (calendar.timegm(entry['timestamp'].timetuple())
            + entry['timestamp_ms'] / 1000
            - entry['system_timestamp']
            - entry['system_timestamp_ms'] / 1000)

def print_camera_events(fitfile,zero_time):
    required_fields = ['timestamp','timestamp_ms','camera_event_type']
    data = find_fields(fitfile,required_fields)
    for entry in data:
        print('%f %s' % (zero_time + entry['timestamp'] + entry['timestamp_ms'] / 1000, entry['camera_event_type']))

def find_fields(fitfile,req_fields):
    messages = fitfile.messages
    data = []
    for m in messages:
        skip=False
        if not hasattr(m, 'fields'):
            continue
        fields = m.fields
        mdata = {}
        for field in fields:
            if field.name in req_fields:
                mdata[field.name] = field.value
        for rf in req_fields:
            if rf not in mdata:
                skip=True
        if not skip:
            data.append(mdata)
    return data

if __name__=='__main__':
    main()

Selgituseks siia juurde veel:

  • FIT failid kasutavad sisemiselt kahte erinevat ajatemplit - faili suhteline aeg ning absoluutaeg. Nende omavaheline seostamine on jäetud faili parsija hooleks. Suhteline aeg hakkab lugema siis kui andmete talletamine alguse saab. Kui kaamera GPS signaali kätte saab, siis lisatakse logisse üks rida, mis absoluutaja ja suhtelise aja omavahel kokku viib. Ülal tegeleb selle info välja lugemisega funktsioon find_zero_timestamp.
  • Tasub eraldi rõhutamist, et FIT faili absoluutne ajatempel ei ole sama, mis Unix TimeStamp. FIT faili aeg hakkab lugema 31. detsembrist 1989, UTC aja järgi kell 0:00 öösel. Samas ülal skriptis seda teisendust teha ei ole vaja - näib, et fitparse teek teisendab tulemuse ise juba Unix Timestamp-iks.

Wrapper script

Kuna meie puhul on pigem mõistlik paljudest FIT failidest pärit kaamerasündmused kõik koos välja trükkida ning ühte faili kirjutada, siis sai loodud wrapper script, mis selle asja ära teeb. Nimelt ei ole videofailide masstöötlemisel praktiline otsida kaamerasündmuseid iga kord otse FIT failidest, vaid soovime seda teha juba töödeldud TXT failidest. Põhjus selleks peitub selles, et meil ei ole töökindlat meetodit, kuidas konkreetne FIT fail erinevate kaamerate ja kasutusviiside korral automaatselt videoga ära seostada ning kogu FIT failide kausta parsimine iga video töötlemisel võtaks lihtsalt liiga palju aega. Seetõttu sobib toimivaks lahenduseks see, kui kirjutame kõik kaamerasündmused läbi kogu tegevuse ühte faili kokku ning seejärel parsime video töötlemisel juba seda kaamerasündmuste nimekirja.

#!/bin/bash

EXTENSION="fit"
OUTPUT_FILE="cameraevents.txt"
rm -f "$OUTPUT_FILE"

while IFS= read -r -d '' -u 9
do
    echo "Extracting camera events from $REPLY"
    python3 print_fit_camera_events.py "$REPLY" >> "$OUTPUT_FILE"
done 9< <( find . -type f -iname "*.$EXTENSION" -print0 )

Näidis, kuidas cameraevents.txt välja näha võiks:

1523710344.576000 video_second_stream_start
1523710344.655000 video_start
1523711244.695000 video_split
1523711244.855000 video_second_stream_split
1523711412.175000 video_end
1523711412.135000 video_second_stream_end
1525171604.478000 photo_taken

Video ajatemplite teadasaamine

Eeldame, et meil on olemas (eelmises peatükis loodud) fail cameraevents.txt, mis katab ka käesoleva videoga seotud kaamerasündmused.

Selleks, et saada teada täpne aeg, mil meid huvitava video esimene kaader filmiti, võib kasutada järgmist skripti. Et skripti nimi oleks kooskõlas edasi toodud kasutusjuhtudega, peaks skripti talletama faili get_video_start_from_camera_events.

#!/bin/bash

# Arguments: <Approximate video start time as Unix Timestamp>

EVENTSFILE="cameraevents.txt"

STARTEVENTS=`egrep --color=never 'video_start|video_split' "$EVENTSFILE" | cut -d ' ' -f1`

while read -r line; do
    if (( $(echo "$1 + 5 > $line" | bc -l) )); then
        if (( $(echo "$1 - 5 < $line" | bc -l) )); then
           echo $line
        fi
    fi
done <<< "$STARTEVENTS"

Täiendavad märkmed:

  • Skript ootab parameetrina umbkaudset aega, mil video filmimine alguse võis saada ning trükib välja täpse (koos komakohtadega) ajatempli, mõlemad Unix Timestamp-ina. Hetkel lubab skript umbkaudse aja viga ±5 sekundi osas.
  • Sisendfailist cameraevents.txt filtreeritakse välja ainult sündmused "video_start" ja "video_split". Lõpu aeg meid ei huvita, sest selle saab skripti välja kutsuja ise välja arvutada, kui liidab video algusele video pikkuse. Seni tehtud testid näitavad, et need numbrid lähevad hästi kokku.
  • Kui peaks tekkima soov siinse skriptiga just video lõpu aegu tuvastada, siis tuleks jälgida sündmusi "video_end" ja "video_split". Ehk siis "video_split" sündmust tuleb tegelikult käsitleda nii, et sel hetkel toimus nii sündmus "video_start" kui ka "video_end". Sel on ka loogiline põhjendus - kui kaamera tol hetkel video tükeldas ning uut faili alustas, siis oligi see moment nii ühe video lõpuks kui teise alguseks.

Seni testitud Garmin seikluskaamerate Virb X ja Virb 360 korral tundub, et kõige mõistlikum on video umbkaudset algust hinnata nii, kui faili Last modified ajatemplist lahutada maha video pikkus. Ehk siis kokkuvõttes saab video täpse algusaja teada järgnevalt ($REPLY on seal videofaili nimi):

MODIFIED=`stat -c %Y "$REPLY"`
DURATION=`exiftool -S -s -n -trackduration "$REPLY"`

# First get an estimation
CREATEDATE_APPROX=`echo "$MODIFIED-$DURATION" | bc`

# Then read exact start time
CREATEDATE_PRECISE=`./get_video_start_from_camera_events "$CREATEDATE_APPROX"`

Videole vastava GPX faili loomine

Kui video alguse ja lõpu ajatemplid on teada, võib FIT failist pärit rajalogi näiteks GPSBabel abil selle järgi ära lõigata ning tekitada videole vastava GPX faili. Tasub siiski märkimist, et nii mõnelgi juhul ei saa kaamera GPS signaali kätte kohe video kõige esimesest hetkest alates, seega ainult lõikamisest jääb väheks. Samuti ei aita sellisel juhul puuduvate punktide juurde interpoleerimine - vaja oleks mingit referentsi, kust poolt kaamera kasutaja esimesele talletunud rajalogi punktile lähenes.

Lahenduseks on variant, et kui lisaks kaamerale on talletatud rajalogid ka nutitelefoni ja/või autokaameraga, siis tuleks eelnevalt panna kokku reisi nn master rajalogi, mis sisaldab kõigi seadmete "parimaid palu" ning tükeldada hoopis seda.

Hetkel on taoline lahendus olemas skriptikogumiku tasemel, eraldi artikkel on TODO.

Andmete eksport tabelisse

Kui on soov vaadata FIT faili sisu tabelina, et saada aru, mis andmed ja mis kujul seal üldse olemas on, võib kasutada Java-põhist FIT SDK tarkvarapaketti. Kuigi Java võiks teoreetiliselt toimida mistahes platvormil, siis kaasnevad skriptid olid seal BAT failidena ning seetõttu sai antud paketti proovitud Windowsi peal.

Peale alla laadimist ja lahti pakkimist võib kaustast java leida faili FitToCSV.bat. Kui anda sellele parameetriks FIT fail, siis konverteeritakse see CSV formaati, mille võib omakorda Exceli või LibreOffice'ga avada.