Die Terrasse ist repariert -- und das Netz ist besser als seine Redundanz

Erster Lauf mit beiden Ping-Strecken (23:30-23:41, per Stopp-Knopf beendet).

TERRASSE, die Frage des Aufbaus:
  2026-08-21, ap2 tief:      43 % Verlust, laengste Luecke 20,4 s, -72 dBm
  2026-08-22, ap2 auf 2,5 m:  9 % Verlust, laengste Luecke  1,0 s, -69,0 dBm
Gleiches Geraet, gleiche Rolle. 1,0 s liegt unter der Hoerbarkeitsschwelle im
Videocall. Der Dead Spot, der TP25 seit dem 08-20 getragen hat, ist weg.

STRUKTURBEFUND: die Referenz hat in 17 von 21 Raumabschnitten 0 % Verlust --
die Alltagsabdeckung ist ueberall in Ordnung. Der Gegen-AP traegt dagegen fast
nirgends: Schlafzimmer -84,6/38 %, Bad -82,3/24 %, Kueche -82,0/23 %, Terrasse
-78,7/19 %. Faellt ein AP aus, ist die halbe Wohnung "verbunden, aber tot".
Ein dritter AP kaufte damit REDUNDANZ, keine Abdeckung -- nach Christophs
eigener Randbedingung derzeit nicht begruendet.

KORREKTUR zum 22:43-Lauf: "die Kueche ist ein Loch fuer beide APs" gilt nur
fuer die aeusserste Ecke. Hier hat die Referenz in der Kueche 1 % Verlust bei
-71 dBm. Der Unterschied ist der Weg -- der Vorlauf ging ausdruecklich allen
Waenden entlang in die totesten Winkel. Beide Aussagen gelten, sie messen
verschiedene Orte.

Fund E weiter abgestuft: der Sticky-Client vom 08-20 reproduzierte sich nicht
(Treppe: sauberer Wechsel ap2/5G -> ap1/5G, 0 % ueber 57 s). Nicht umsetzen,
bevor ein Sticky-Fall erneut gemessen ist. Ein Lauf widerlegt Fund B nicht,
er zeigt nur, dass es nicht der Normalfall ist.

WERKZEUG: die Auswertung wies die groesste Sonden-Luecke auf der Treppe als
"⚠️ eher Ruhezustand" aus (4,6 s). Das war unser eigener Eingriff -- um
23:35:10 wechselte der Pin, das S22 war 23:35:12-16 abgemeldet. Ein Artefakt
als "Geraet war erreichbar, wollte nur nicht" zu beschriften ist doppelt
falsch. Pin-Wechsel werden jetzt erkannt und als solche benannt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-22 23:45:27 +02:00
parent ac4bce8cc2
commit a94aa92826
16 changed files with 7513 additions and 4 deletions

View File

@@ -136,16 +136,25 @@ def geraete(run_dir):
("Sonde (Gegen-AP)", sonde_ping, sonde_funk)]
def auswerten(ping, funk, start, stop):
def auswerten(ping, funk, start, stop, pin_zeiten=()):
"""Verlust, laengste Luecke, Signal und APs eines Geraets in einem Zeitfenster."""
p = [x for x in ping if start <= x[0] < stop]
f = [x for x in funk if start <= x[0] < stop]
luecke, verloren = 0.0, 0
luecke, verloren, luecke_ab = 0.0, 0, None
for i in range(1, len(p)):
miss = p[i][1] - p[i - 1][1] - 1
if miss > 0:
verloren += miss
luecke = max(luecke, miss * 0.2)
if miss * 0.2 > luecke:
luecke, luecke_ab = miss * 0.2, p[i - 1][0]
# 🔴 Die groesste Luecke der Sonde ist oft KEIN Funkbefund, sondern unser eigener
# Eingriff: beim Umschalten des Gegen-AP wird das Geraet per Deauth geworfen und
# braucht ein paar Sekunden zurueck. Am 2026-08-22 stand deshalb auf der Treppe
# "4,6 s ⚠️ eher Ruhezustand" -- das Geraet war von 23:35:12 bis 23:35:16 schlicht
# abgemeldet, weil um 23:35:10 der Pin von ap1 auf ap2 wechselte. Ein Messartefakt
# als "Geraet war erreichbar, wollte nur nicht" zu beschriften ist doppelt falsch.
pin_luecke = luecke_ab is not None and any(
luecke_ab - 3 <= t <= luecke_ab + luecke + 3 for t in pin_zeiten)
gesamt = (p[-1][1] - p[0][1] + 1) if len(p) > 1 else 0
sig = [x[2] for x in f]
raten = [x[3] for x in f if x[3]]
@@ -164,6 +173,7 @@ def auswerten(ping, funk, start, stop):
"sig": sig, "luecke": luecke, "verloren": verloren, "gesamt": gesamt,
"ifaces": sorted({AP.get(x[1], x[1]) for x in f if x[1]}),
"schlaefrig": schlaefrig, "verdacht": verdacht, "stumm": stumm, "gut": gut,
"pin_luecke": pin_luecke,
"leer": not p and not f,
}
@@ -187,6 +197,9 @@ def main():
mk = urllib.parse.unquote(m["marker"])
if mk.upper().startswith("RAUM="):
rooms.append((sec(m["ts_local"]), mk[5:].title()))
pin_zeiten = [sec(m["ts_local"])
for m in csv.DictReader(open(os.path.join(a.dir, "markers.csv")))
if urllib.parse.unquote(m["marker"]).upper().startswith("PIN=")]
if not rooms:
raise SystemExit("Keine RAUM=-Marken in markers.csv — nichts zuzuordnen.")
@@ -204,7 +217,9 @@ def main():
print("-" * 100)
for start_s, stop_s, name in fenster:
werte = [(rolle, auswerten(ping, funk, start_s, stop_s)) for rolle, ping, funk in gs]
werte = [(rolle, auswerten(ping, funk, start_s, stop_s,
pin_zeiten if "Sonde" in rolle else ()))
for rolle, ping, funk in gs]
if all(w["leer"] for _, w in werte):
continue
erste = True
@@ -214,6 +229,8 @@ def main():
sig, luecke = w["sig"], w["luecke"]
if w["stumm"]:
lz = "⚠️ Display gesperrt?" if w["gut"] else "⚠️ keine Antwort"
elif luecke and w["pin_luecke"]:
lz = "%.1f s ⚠️ Pin-Wechsel" % luecke
elif luecke and w["schlaefrig"]:
lz = "%.1f s ⚠️ eher Ruhezustand" % luecke
elif luecke and w["verdacht"]:
@@ -237,6 +254,8 @@ def main():
print(" erreichbar und hat nur nicht geantwortet (iOS mit gesperrtem Display).")
print("⚠️ evtl. Ruhezustand = dasselbe Muster, aber ohne PHY-Rate zum Gegenprüfen")
print(" (die Referenz liefert nur AP und Signal). Verdacht, kein Befund.")
print("⚠️ Pin-Wechsel = die Lücke fällt mit dem Umschalten des Gegen-AP zusammen. Das ist")
print(" unser eigener Eingriff (Deauth + Neuanmeldung), kein Funkbefund. Nur die Sonde.")
print("⚠️ Display gesperrt? = Funkverbindung stand mit gutem Signal, aber KEINE einzige")
print(" Ping-Antwort. Das ist kein Funkloch und keine Datenlücke — bei iOS beantwortet")
print(" ein gesperrtes Display kein ICMP. Beim nächsten Lauf Display anlassen.")