Fahrtstrecke auf 100 m: CAN als Anker, GNSS als Nachkommastelle (2026.9.1.10)

Zwei echte Fahrten am 01.09.2026 haben die Datenform geliefert, auf die
2026.9.1.5 gewartet hat.

strecke_aus_zaehler() summiert die Zuwaechse des geraeteeigenen
Kilometerzaehlers und behandelt einen Ruecksprung als Ruecksetzung. Weder der
rohe Zaehler noch die Teilstrecken taugen allein: der Zaehler verliert bei
einer Ruecksetzung alles Vorherige (Fahrt 1: 0,415 km), die Teilstrecken
verlieren einzelne Datensaetze (Fahrt 2: 4 von 50 null, 0,2 km zu wenig). An
jedem Datensatz sind beide identisch - sie gehen nur verschieden kaputt.

_gnss_verfeinern() ersetzt die grobe Strecke nur, wenn die feine innerhalb der
Rundungsunschaerfe des Ankers liegt (GNSS_TOLERANZ_KM = 1.0; beide Enden des
CAN-Werts sind auf ganze Kilometer gerundet). Sonst behaelt der Fahrzeugwert
recht.

VERIFIZIERT gegen die echten Fahrten:
- Fahrt 1 (mit Ruecksetzung): 6,958 km, Google Maps sagt 7,1
- Fahrt 2 (sauber): 6,816 km
- live: 'Strecke auf 6.896 km verfeinert (Kilometerstand sagte 7.0 km)'
- drei aeltere Fahrten korrekt abgelehnt (209178 km gegen 21 km Anker)
- sechs Randfaelle: zwei Ruecksetzungen, ein Punkt, leer, unlesbare Werte

Nebenbei bestaetigt: NACHLAUF_S = 900 (Zuendung aus 16:33:39, Trip-Signal aus
16:48:44, gespeichertes Ende 16:33:36); der Rueckwaertssprung-Schutz griff live
beim Fahrzeugwechsel (-147361 km verworfen, Fahrt blieb offen); die
RPM-Zuendung prellt nicht; das Bewegungstor meldet wieder Stillstand.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-09-01 17:31:33 +02:00
co-authored by Claude Opus 5
parent 0124254ea9
commit af83805c34
7 changed files with 186 additions and 4 deletions
@@ -272,6 +272,51 @@ def route_aus_verlauf(
return punkte if len(punkte) >= 2 else None
# Wie weit die GNSS-Strecke vom Kilometerstand des Fahrzeugs abweichen darf.
#
# Beide Enden des Kilometerstands sind auf ganze Kilometer gerundet (am
# echten Fahrzeug nachgemessen: 147 Schritte von exakt 1,0 km, kein einziger
# feinerer). Die wahre Strecke liegt damit irgendwo in einem Fenster von
# +/- 1 km um die Differenz. Was darin liegt, ist mit dem Fahrzeugwert
# vereinbar; was darueber hinausgeht, ist es nicht - dann stimmt etwas nicht,
# und der Fahrzeugwert behaelt recht.
GNSS_TOLERANZ_KM = 1.0
def strecke_aus_zaehler(punkte: list[Verlaufspunkt]) -> float | None:
"""Gefahrene Strecke aus einem laufenden Kilometerzaehler des Geraets.
Summiert die Zuwaechse statt einfach Ende minus Anfang zu rechnen, weil der
Zaehler zurueckspringen kann. Ein Ruecksprung ist kein Rueckwaertsfahren,
sondern eine Ruecksetzung - der neue Wert ist dann selbst der Zuwachs seit
der Ruecksetzung, alles davor wurde bereits gezaehlt.
Am 01.09.2026 an zwei echten Fahrten gemessen:
* Fahrt 1, mit Ruecksetzung mitten in der Fahrt (das Geraet uebernahm
seinen konfigurierten Startwert 0, als der CAN-Kilometerstand zum ersten
Mal hochzaehlte): 0,415 km vor der Ruecksetzung, 6,543 km danach - macht
6,958 km. Google Maps sagt 7,1 km fuer dieselbe Strecke. Ohne die
Ruecksprungbehandlung waeren es 6,543 km gewesen, also 0,4 km zu wenig.
* Fahrt 2, ohne Ruecksetzung: 6,816 km. Dort degeneriert die Summe zu
Ende minus Anfang, wie es sein soll.
Bewusst NICHT ueber die Teilstrecken je Datensatz (segment_mileage), die
dasselbe Geraet mitliefert: die verlieren einzelne Datensaetze. In Fahrt 2
waren 4 von 50 Teilstrecken null, obwohl gefahren wurde, und die Summe lag
dadurch 0,2 km zu niedrig. Der Zaehler zaehlt ueber solche Aussetzer hinweg.
None, wenn weniger als zwei Zahlenwerte vorliegen."""
werte = [zahl(wert) for _ts, wert in punkte]
werte = [w for w in werte if w is not None]
if len(werte) < 2:
return None
strecke = 0.0
for vorher, jetzt in zip(werte, werte[1:]):
strecke += jetzt if jetzt < vorher else jetzt - vorher
return round(strecke, 3)
def hoechstwert_im_fenster(
verlauf: list[Verlaufspunkt],
start: datetime.datetime,