Tools: Auswertung der Binärlogs¶
Der Fahrradcomputer schreibt seine Messdaten als Sequenz fester Binärsätze
auf die SD-Karte (Format v2, siehe src/LogRecords.h).
In Tools/ liegt alles, was damit offline weiterarbeitet.
| Format | Satzgröße | Inhalt |
|---|---|---|
| v1 | 56 Byte | nur Fahrdaten (alle 5 s) -- ältere Logs, wird weiter gelesen |
| v2 | 64 Byte | Typ-Byte an Offset 30: 0 = Fahrdaten, 1 = Wegequalität (pro Intervall, 1..10 s), 2 = Stoß, 3 = manuelles Wege-Label, 4 = Fahrzustand |
Die Versionsnummer steht in jedem Satz an Offset 31. Unbekannte Satztypen
werden übersprungen (und in bikelog info gezählt).
Manuelle Wege-Labels (Typ 3, ohne Versionssprung nachgerüstet): Untergrund
(1 Asphalt, 2 Schotter, 3 Waldweg, 4 Feldweg, 5 Pflaster, 6 Sonstiges) und
Qualität 1 (am besten) bis 4, gesetzt auf dem RQ-Ride-Screen oder per
rq label <untergrund>,<qualität>. Ein Satz bei jedem Wechsel, alle 60 s zur
Sicherheit wiederholt und bei Start/Stopp eines Rohmitschnitts; das Label gilt
bis zum nächsten Satz. Zusätzlich steht es in jedem Stoß-Satz und jedem
Rohdaten-Block. bikelog info stellt die Labels der automatischen Klasse
gegenüber (Strecke, Rauheit-Median und Klassenverteilung je Label). Die Zuordnung
zu OSM surface/smoothness steht in bikelog/record.py (SURFACE_OSM,
LABEL_QUALITY_OSM).
bikelog/ Bibliothek + CLI (reine Standardbibliothek, kein venv nötig)
BikeLogService/ Dienst: holt Sitzungen vom BC ab (mDNS), GPX/CSV (FastAPI, eigene venv)
pyproject.toml macht Tools/ installierbar (für den Dienst, siehe BikeLogService/install.sh)
tests/ pytest-Suite für beides
ReadTachoBin.py Altbekannter CSV-Konverter, jetzt Wrapper um "bikelog csv"
csv2influx.py Unverändert: CSV nach InfluxDB
gpxenrich/ GPX-Track -> Route für TrailBridge: Abbiegehinweise von BRouter, Wegpunkte als
benannte Hinweise (Beschreibung im Kopf von gpx_enrich.py)
Dateien auf der SD-Karte¶
Jeder Boot ist eine Sitzung. Sie schreibt zunächst ins Arbeitsverzeichnis
/BIKECOMP/CUR/, benannt nach einer laufenden Nummer -- beim Booten ist die
Uhr meist noch nicht gestellt:
| Datei | Inhalt |
|---|---|
L_0042.bin |
Binärlog (dieses Format) |
D_0042.log, N_0042.log |
Debug-Log, Forumslader-NMEA |
S_0042.bin, R_0042_NN.bin |
Rohdaten, siehe unten |
T_0042.txt |
Zeit-Hinweise: start <epochMs> <gültig 0/1> <uptimeMs>, dann je Uhrsprung step <ntp\|gps\|?> <offsetMs> <neueEpochMs> <uptimeMs> |
Nach dem nächsten Boot räumt ein Hintergrund-Task (src/LogSessions.cpp) jede
ältere Sitzung aus CUR/ weg:
- Startzeit aus
T_*; lief die Sitzung ohne Uhr (1970) an, wird sie um den Sprung korrigiert, mit dem NTP oder die GPS-Zeit von TrailBridge die Uhr gültig gemacht hat. - Ziel
/BIKECOMP/JJJJMMTT/X_HHMMSS.*, ohne ermittelbare Zeit/BIKECOMP/NO_TIME/X_0042.*. ImL_*.binwerden dabei die 1970-Zeitstempel umgeschrieben; Rohdaten und Debug-Log behalten ihre Originalzeiten. I_HHMMSS.txt: Kurzstatistik alsschlüssel=wert-Zeilen (Strecke, Dauer, Fahrzeit, Ø/max. Geschwindigkeit, GPS-Anteil, Anzahl Wegequalitäts-Intervalle, Stöße, Labels, Rohmitschnitte;corr_ms= angewandte Zeitkorrektur). Die Logfile-Seite des Webservers zeigt sie je Sitzung an.- Leere Dateien werden gelöscht.
Ältere Logs (vor dieser Umstellung) liegen weiter als L0042.bin in
NO_TIME/ bzw. datiert ohne I_*.txt.
CLI¶
Ohne Installation, direkt aus Tools/:
python3 -m bikelog info -i /pfad/L0001.bin # Übersicht über ein Log
python3 -m bikelog csv -i /pfad/L0001.bin -o out.csv [--gps] [--roadq]
python3 -m bikelog csv -i /pfad/L0001.bin -o out.csv --roadq-out wege.csv --shocks-out stoesse.csv --labels-out labels.csv
python3 -m bikelog gpx -i /pfad/L0001.bin -o tour.gpx [--no-shocks] [--min-severity 2]
./ReadTachoBin.py -i /pfad/L0001.bin -o out.csv # wie bisher
GPX-Export¶
Standardmäßig „reich“: neben Garmins TrackPointExtension v2 (Puls,
Trittfrequenz, Temperatur, Geschwindigkeit, Kurs) trägt jeder Trackpunkt eine
eigene Erweiterung mit Steigung, Wegequalität, Label usw., Label-Wechsel
werden Wegpunkte, die Metadaten enthalten die Kennzahlen der Fahrt -- siehe
den Log-Dienst. --plain
lässt die eigenen Erweiterungen weg, --no-labels die Label-Wegpunkte.
Das XML ist der einfache Teil; entscheidend ist, was nicht hineinkommt:
| Filter | Standard | Option |
|---|---|---|
| Datensätze ohne GPS-Fix | immer raus | -- |
| veraltete Fixes (Heartbeat-Wiederholung im Tunnel) | > 5000 ms raus | --max-fix-age |
| Zeitstempel vor 2020 (Uhr nicht per NTP gesetzt) | immer raus | -- |
| Position exakt (0, 0) | immer raus | -- |
| zu ungenaue Fixes | aus | --max-accuracy |
Zeitlücke → neues <trkseg> |
> 60 s | --segment-gap |
Höhenquelle über --ele=auto|baro|gps; auto nimmt den Barometer, wenn sein
Wert plausibel ist, sonst die GPS-Höhe. Puls 0 und 255 heißen „kein Wert“ und
werden weggelassen.
Stöße (v2) werden als <wpt> ausgegeben -- Name Stoß 5,2 g, <type>
shock-1..3 nach Schwere, in <desc> zweiter Peak (Hinterrad) und
Geschwindigkeit. Für sie gelten dieselben Positions- und Zeitfilter wie für
die Trackpunkte. Die Wegequalitäts-Intervalle selbst (mit allen Feldern) gibt es weiter
nur als CSV (--roadq-out); im GPX steht an jedem Trackpunkt Klasse und
Rauheit des Intervalls, in das er fällt.
Jeder Lauf meldet, was verworfen wurde:
195/243 Punkte in 2 Segment(en), 20 verworfen (ohne Fix), 25 verworfen (veraltet)
Rohdaten des Beschleunigungssensors¶
Neben dem Log einer Sitzung L_HHMMSS.bin können zwei Rohdaten-Dateien liegen
(Format: src/RawCapture.h, 400 Hz, rohe LSB):
| Datei | Entsteht | Größe |
|---|---|---|
R_HHMMSS_NN.bin |
auf Anforderung: rq raw <s> (Serial), Buttons auf /debug/imu oder Aufnahme-Taste des RQ-Ride-Screens, bis 1800 s |
ca. 150 KB/min |
S_HHMMSS.bin |
immer: 0,25 s vor bis 0,5 s nach jedem geloggten Stoß | ca. 1,8 KB/Stoß |
python3 -m bikelog raw info -i R_143012_01.bin
python3 -m bikelog raw csv -i R_143012_01.bin -o raw.csv # Frames in g
python3 -m bikelog raw replay -i R_143012_01.bin shock=2.5 interval=1 --rq-out rq.csv
python3 -m bikelog raw replay -i S_143012.bin # jeden Stoß nachrechnen
replay rechnet mit dem Firmware-Code selbst (src/RoadQuality.cpp),
nicht mit einer Python-Nachbildung: Es baut beim ersten Aufruf
rqreplay/rq_replay.cpp mit g++ nach .build/ und
übergibt die Parameter (python3 -m bikelog raw replay -h listet sie). So
lassen sich Schwellen und Filter an echten Aufnahmen durchprobieren, bevor
sie in die Firmware wandern. Das manuelle Label jedes Blocks läuft mit durch:
raw info listet die Label-Abschnitte, raw replay zeigt Rauheit und Klassen
je Label -- der direkte Vergleich „gefühlt“ gegen „gemessen“.
Testdaten ohne Hardware¶
python3 -m bikelog fixture synth -o ride.bin --unset-clock 3 [--roadq]
python3 -m bikelog fixture from-gpx tour.gpx -o ride.bin
synth erzeugt absichtlich die unangenehmen Fälle (Start ohne Fix, Tunnel
mit veralteten Fixes, Pause als Zeitlücke, ungesetzte Uhr). from-gpx baut
aus einer echten Tour -- z. B. einem Komoot-Export -- ein Binärlog mit
realistischer Geometrie, solange es noch keine echten Aufzeichnungen gibt.
Tests¶
python3 -m venv .venv
.venv/bin/pip install -r BikeLogService/requirements.txt
.venv/bin/python -m pytest
tests/test_logformat.py pinnt das Binärlayout an die Firmware:
test_layout_matches_firmware_header kompiliert src/LogRecords.h mit g++
auf dem Host (logrecords_dump.cpp),
lässt je einen Satz pro Typ schreiben und liest ihn zurück. Schlägt er fehl,
haben sich die Firmware-Structs geändert und bikelog/record.py braucht
neue Einträge in FORMATS (und die Firmware eine neue FORMAT_VERSION).
Ohne g++ wird der Test übersprungen.