feat(bridge): Fahrtrichtung (Himmelsrichtung) im Standort-Präfix
Aus dem permanenten GPS wird die Bewegungsrichtung berechnet: Bearing zwischen einem Anker-Punkt und der aktuellen Position, gemappt auf 8-Wind-Adverb (nordwestwaerts …). Der Anker rueckt erst bei echter Bewegung (>25 m) nach, sonst zaehlt GPS-Jitter im Stand nicht — Stehenbleiben behaelt die letzte Richtung, Umdrehen liefert automatisch die neue. Praefix jetzt z.B.: [Stefans aktueller Standort: A 28 bei km 55,6, Oldenburg, Niedersachsen (GPS 53.12, 8.22), unterwegs nordwestwaerts. ...] Reiner Bridge-Fix (Haversine + Bearing lokal, kein Dienst noetig). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+53
-4
@@ -16,6 +16,7 @@ import asyncio
|
|||||||
import base64
|
import base64
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
import math
|
||||||
import mimetypes
|
import mimetypes
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
@@ -371,6 +372,37 @@ def clean_text_for_tts(text: str) -> str:
|
|||||||
return t.strip()
|
return t.strip()
|
||||||
|
|
||||||
|
|
||||||
|
# ── Geo: Bewegungsrichtung aus zwei GPS-Punkten ──────────────
|
||||||
|
|
||||||
|
# Fahrtrichtung als Himmelsrichtungs-Adverb (8-Wind, "…waerts"). Index =
|
||||||
|
# gerundeter Bearing / 45 (mod 8).
|
||||||
|
_COMPASS_ADV = ["nordwaerts", "nordostwaerts", "ostwaerts", "suedostwaerts",
|
||||||
|
"suedwaerts", "suedwestwaerts", "westwaerts", "nordwestwaerts"]
|
||||||
|
|
||||||
|
|
||||||
|
def _haversine_m(lat1: float, lon1: float, lat2: float, lon2: float) -> float:
|
||||||
|
"""Distanz in Metern zwischen zwei GPS-Punkten."""
|
||||||
|
r = 6371000.0
|
||||||
|
p1, p2 = math.radians(lat1), math.radians(lat2)
|
||||||
|
dp = math.radians(lat2 - lat1)
|
||||||
|
dl = math.radians(lon2 - lon1)
|
||||||
|
a = math.sin(dp / 2) ** 2 + math.cos(p1) * math.cos(p2) * math.sin(dl / 2) ** 2
|
||||||
|
return 2 * r * math.asin(min(1.0, math.sqrt(a)))
|
||||||
|
|
||||||
|
|
||||||
|
def _bearing_deg(lat1: float, lon1: float, lat2: float, lon2: float) -> float:
|
||||||
|
"""Kompass-Peilung 0..360 (0=Nord) von Punkt1 nach Punkt2."""
|
||||||
|
p1, p2 = math.radians(lat1), math.radians(lat2)
|
||||||
|
dl = math.radians(lon2 - lon1)
|
||||||
|
y = math.sin(dl) * math.cos(p2)
|
||||||
|
x = math.cos(p1) * math.sin(p2) - math.sin(p1) * math.cos(p2) * math.cos(dl)
|
||||||
|
return (math.degrees(math.atan2(y, x)) + 360.0) % 360.0
|
||||||
|
|
||||||
|
|
||||||
|
def _heading_word(bearing: float) -> str:
|
||||||
|
return _COMPASS_ADV[round(bearing / 45.0) % 8]
|
||||||
|
|
||||||
|
|
||||||
# ── STT Engine ───────────────────────────────────────────────
|
# ── STT Engine ───────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
||||||
@@ -1176,8 +1208,23 @@ class ARIABridge:
|
|||||||
"lat": float(lat),
|
"lat": float(lat),
|
||||||
"lon": float(lon),
|
"lon": float(lon),
|
||||||
})
|
})
|
||||||
|
lat, lon = float(lat), float(lon)
|
||||||
# Fuer den Standort-Praefix merken (auch fuer Turns ohne frisches GPS).
|
# Fuer den Standort-Praefix merken (auch fuer Turns ohne frisches GPS).
|
||||||
self._last_location = {"lat": float(lat), "lon": float(lon)}
|
self._last_location = {"lat": lat, "lon": lon}
|
||||||
|
# Fahrtrichtung aus dem Bewegungsvektor. Anker bleibt stehen bis wir
|
||||||
|
# uns >MIN_MOVE_M wirklich wegbewegt haben — so zaehlt echtes Fahren,
|
||||||
|
# nicht das GPS-Jitter im Stand. Stehen wir → letzte Richtung bleibt.
|
||||||
|
# Umdrehen liefert automatisch neue Punkte → neue Richtung.
|
||||||
|
MIN_MOVE_M = 25.0
|
||||||
|
anchor = getattr(self, "_heading_anchor", None)
|
||||||
|
if anchor:
|
||||||
|
moved = _haversine_m(anchor["lat"], anchor["lon"], lat, lon)
|
||||||
|
if moved >= MIN_MOVE_M:
|
||||||
|
self._last_heading = _heading_word(
|
||||||
|
_bearing_deg(anchor["lat"], anchor["lon"], lat, lon))
|
||||||
|
self._heading_anchor = {"lat": lat, "lon": lon}
|
||||||
|
else:
|
||||||
|
self._heading_anchor = {"lat": lat, "lon": lon}
|
||||||
except Exception:
|
except Exception:
|
||||||
return
|
return
|
||||||
# Ortsname im Hintergrund aufloesen (Nominatim, gecacht) — kein Modell.
|
# Ortsname im Hintergrund aufloesen (Nominatim, gecacht) — kein Modell.
|
||||||
@@ -1700,11 +1747,13 @@ class ARIABridge:
|
|||||||
# → nur Koordinaten (Claude kann die zur Not selbst deuten).
|
# → nur Koordinaten (Claude kann die zur Not selbst deuten).
|
||||||
place = getattr(self, "_last_place_name", "")
|
place = getattr(self, "_last_place_name", "")
|
||||||
where = f"{place} " if place else ""
|
where = f"{place} " if place else ""
|
||||||
|
heading = getattr(self, "_last_heading", "")
|
||||||
|
moving = f", unterwegs {heading}" if heading else ""
|
||||||
parts.append(
|
parts.append(
|
||||||
f"[Stefans aktueller Standort: {where}(GPS {float(lat):.5f}, "
|
f"[Stefans aktueller Standort: {where}(GPS {float(lat):.5f}, "
|
||||||
f"{float(lon):.5f}). Nutze das nur wenn die Frage sich auf seinen "
|
f"{float(lon):.5f}){moving}. Nutze das nur wenn die Frage sich "
|
||||||
f"Standort bezieht. Erwaehne ihn nicht von dir aus, ausser er "
|
f"auf seinen Standort/seine Fahrtrichtung bezieht. Erwaehne es "
|
||||||
f"fragt explizit danach.]"
|
f"nicht von dir aus, ausser er fragt explizit danach.]"
|
||||||
)
|
)
|
||||||
if parts:
|
if parts:
|
||||||
return " ".join(parts) + " " + text
|
return " ".join(parts) + " " + text
|
||||||
|
|||||||
Reference in New Issue
Block a user