From ddb1bfac6ae28185d2829bc525ee88dea6449e4f Mon Sep 17 00:00:00 2001 From: duffyduck Date: Sat, 11 Jul 2026 22:11:07 +0200 Subject: [PATCH] =?UTF-8?q?feat(bridge):=20Fahrtrichtung=20(Himmelsrichtun?= =?UTF-8?q?g)=20im=20Standort-Pr=C3=A4fix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- bridge/aria_bridge.py | 57 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/bridge/aria_bridge.py b/bridge/aria_bridge.py index 96d9cbd..cadb08d 100644 --- a/bridge/aria_bridge.py +++ b/bridge/aria_bridge.py @@ -16,6 +16,7 @@ import asyncio import base64 import json import logging +import math import mimetypes import os import re @@ -371,6 +372,37 @@ def clean_text_for_tts(text: str) -> str: 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 ─────────────────────────────────────────────── @@ -1176,8 +1208,23 @@ class ARIABridge: "lat": float(lat), "lon": float(lon), }) + lat, lon = float(lat), float(lon) # 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: return # Ortsname im Hintergrund aufloesen (Nominatim, gecacht) — kein Modell. @@ -1700,11 +1747,13 @@ class ARIABridge: # → nur Koordinaten (Claude kann die zur Not selbst deuten). place = getattr(self, "_last_place_name", "") where = f"{place} " if place else "" + heading = getattr(self, "_last_heading", "") + moving = f", unterwegs {heading}" if heading else "" parts.append( f"[Stefans aktueller Standort: {where}(GPS {float(lat):.5f}, " - f"{float(lon):.5f}). Nutze das nur wenn die Frage sich auf seinen " - f"Standort bezieht. Erwaehne ihn nicht von dir aus, ausser er " - f"fragt explizit danach.]" + f"{float(lon):.5f}){moving}. Nutze das nur wenn die Frage sich " + f"auf seinen Standort/seine Fahrtrichtung bezieht. Erwaehne es " + f"nicht von dir aus, ausser er fragt explizit danach.]" ) if parts: return " ".join(parts) + " " + text