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 <noreply@anthropic.com>
This commit is contained in:
@@ -32,9 +32,11 @@ import logging
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from .verlauf import (
|
||||
GNSS_TOLERANZ_KM,
|
||||
UNPLAUSIBLE_KMH,
|
||||
durchschnitt_kmh,
|
||||
hoechstwert_im_fenster,
|
||||
strecke_aus_zaehler,
|
||||
naechster_wert,
|
||||
route_aus_verlauf,
|
||||
verbrauch_aus_literstaenden,
|
||||
@@ -159,6 +161,14 @@ async def durchfuehren(k: Koordinator) -> None:
|
||||
)
|
||||
fahrt["avg_speed_kmh"] = schnitt
|
||||
|
||||
# Erst nach den Kilometerstaenden: die Verfeinerung braucht die grobe
|
||||
# Strecke als Anker und laeuft nur auf Fahrten, die sie schon haben.
|
||||
gnss_sensor = k.zuordnung.werte.GNSS_KM_SENSOR
|
||||
if gnss_sensor:
|
||||
for fahrt in fahrten:
|
||||
if fahrt.get("km_quelle") == "odometer":
|
||||
await _gnss_verfeinern(k, gnss_sensor, fahrt)
|
||||
|
||||
tempo_sensor = k.zuordnung.werte.GESCHWINDIGKEIT_SENSOR
|
||||
if tempo_sensor:
|
||||
ohne_vmax = [f for f in fahrten if f.get("vmax_kmh") is None]
|
||||
@@ -223,6 +233,52 @@ async def _fahrt_screenen(k: Koordinator, km_sensor: str, fahrt: dict) -> None:
|
||||
fahrt.update(aenderungen)
|
||||
|
||||
|
||||
async def _gnss_verfeinern(k: Koordinator, gnss_sensor: str, fahrt: dict) -> None:
|
||||
"""Ersetzt die auf ganze Kilometer gerundete Strecke durch die metergenaue
|
||||
des Geraets - aber nur, wenn beide zueinander passen.
|
||||
|
||||
Der Kilometerstand des Fahrzeugs bleibt der Anker: er ist der Tacho, er
|
||||
driftet nicht, und er ist die Zahl, die der Fahrer im Cockpit sieht. Nur
|
||||
loest er eben ganze Kilometer auf. Der GNSS-Zaehler des Geraets loest auf
|
||||
einen Meter auf, kann aber driften und Empfang verlieren.
|
||||
|
||||
Deshalb keine Ersetzung, sondern eine Pruefung: die feine Zahl gilt, solange
|
||||
sie innerhalb der Rundungsunschaerfe des groben Ankers liegt
|
||||
(GNSS_TOLERANZ_KM). Liegt sie darueber, hat der Anker recht und die feine
|
||||
Zahl wird verworfen - dann fehlte Empfang, oder der Zaehler ist gedriftet.
|
||||
|
||||
Laeuft nur auf Fahrten mit km_quelle == \"odometer\", also genau einmal je
|
||||
Fahrt: danach steht dort \"odometer+gnss\" (oder es blieb bei \"odometer\",
|
||||
wenn nichts Brauchbares vorlag, und ein spaeterer Lauf versucht es erneut,
|
||||
sobald mehr Verlauf da ist)."""
|
||||
start = _als_zeit(fahrt.get("ts_start"))
|
||||
ende = _als_zeit(fahrt.get("ts_end"))
|
||||
grob = fahrt.get("distance_km")
|
||||
if start is None or ende is None or grob is None:
|
||||
return
|
||||
|
||||
punkte = await verlauf_lesen(k.hass, gnss_sensor, start, ende)
|
||||
fein = strecke_aus_zaehler(punkte)
|
||||
if fein is None:
|
||||
return
|
||||
|
||||
if abs(fein - grob) > GNSS_TOLERANZ_KM:
|
||||
_LOGGER.info(
|
||||
"Fahrt %s: GNSS-Strecke %s km weicht um mehr als %s km vom "
|
||||
"Kilometerstand (%s km) ab - der Fahrzeugwert bleibt stehen",
|
||||
fahrt.get("trip_id"), fein, GNSS_TOLERANZ_KM, grob,
|
||||
)
|
||||
return
|
||||
|
||||
aenderungen = {"distance_km": fein, "km_quelle": "odometer+gnss"}
|
||||
await k.ablage.fahrt_aktualisieren(fahrt["trip_id"], aenderungen)
|
||||
fahrt.update(aenderungen)
|
||||
_LOGGER.info(
|
||||
"Fahrt %s: Strecke auf %s km verfeinert (Kilometerstand sagte %s km)",
|
||||
fahrt.get("trip_id"), fein, grob,
|
||||
)
|
||||
|
||||
|
||||
async def _position_screenen(
|
||||
k: Koordinator, lat_sensor: str, lon_sensor: str, fahrt: dict
|
||||
) -> None:
|
||||
|
||||
Reference in New Issue
Block a user