Gehweg-Konsole: Raum-Buttons und Auswertung pro Raum
Die Gehweg-Messung erzeugte bisher eine Zeitachse, aus der niemand ablesen konnte, WO es schlecht war -- der teure Abschnitt von gestern liegt bei "15:07 bis 15:09", und das ist kein Satz, aus dem eine Massnahme folgt. Die Konsole bekommt deshalb ein Raster mit einem Button je Raum; der aktive Raum wird hervorgehoben und die Verweildauer mitgezaehlt. Die Liste steht per TP25_ROOMS in der Umgebung, damit ein fehlender Raum kein Codeeingriff ist. tp25-gehweg-auswertung.py verbindet die drei Mitschnitte -- Ping aufs Telefon, Funkwerte vom Manager, Raum-Marken -- zu einer Tabelle mit Signal, Paketverlust und laengster Luecke je Raum, samt AP, auf dem der Client dort haengt. Dazu die Lehre aus dem ersten Durchgang, diesmal als Anleitung statt als Fussnote: die Konsole gehoert waehrend der Messung auf DAS TELEFON. Der Wake Lock haelt das Display an und das Polling das Geraet wach -- ohne das beantwortet ein iPhone die Pings verzoegert oder gar nicht, und 44 % Verlust sind Ruhezustand statt Funk. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
83
docs/tp25-gehweg-auswertung.py
Normal file
83
docs/tp25-gehweg-auswertung.py
Normal file
@@ -0,0 +1,83 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
TP25 — Gehweg-Auswertung: Signal und Erreichbarkeit pro RAUM.
|
||||
|
||||
Verbindet die drei Mitschnitte der Gehweg-Messung zu einer Raumtabelle:
|
||||
walk-ping.log (5 Pings/s auf das Telefon) -> Verlust und laengste Luecke
|
||||
sampler.csv (Funkwerte am Manager) -> AP/Band und Signal
|
||||
markers.csv (RAUM=-Klicks) -> die Raumgrenzen auf der Zeitachse
|
||||
|
||||
Damit wird aus "irgendwo zwischen 15:07 und 15:09 war es schlecht" ein Satz mit Raumnamen.
|
||||
|
||||
python3 docs/tp25-gehweg-auswertung.py --dir <run-dir>
|
||||
"""
|
||||
import argparse
|
||||
import csv
|
||||
import os
|
||||
import re
|
||||
|
||||
|
||||
def sec(t):
|
||||
h, m, s = map(int, t.split(":"))
|
||||
return h * 3600 + m * 60 + s
|
||||
|
||||
|
||||
AP = {"cap-wifi1": "ap1/5GHz", "cap-wifi2": "ap1/2,4GHz",
|
||||
"cap-wifi3": "ap2/5GHz", "cap-wifi4": "ap2/2,4GHz"}
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--dir", required=True)
|
||||
a = ap.parse_args()
|
||||
|
||||
pings = []
|
||||
for line in open(os.path.join(a.dir, "walk-ping.log")):
|
||||
m = re.match(r"(\d\d:\d\d:\d\d) 64 bytes.*icmp_seq=(\d+).*time=([\d.]+)", line)
|
||||
if m:
|
||||
pings.append((sec(m.group(1)), int(m.group(2)), float(m.group(3))))
|
||||
|
||||
rows = [r for r in csv.DictReader(open(os.path.join(a.dir, "sampler.csv")))
|
||||
if r.get("sta_signal")]
|
||||
|
||||
rooms = []
|
||||
for m in csv.DictReader(open(os.path.join(a.dir, "markers.csv"))):
|
||||
if m["marker"].startswith("RAUM="):
|
||||
rooms.append((int(m["ts_epoch"]), sec(m["ts_local"]), m["marker"][5:]))
|
||||
if not rooms:
|
||||
raise SystemExit("Keine RAUM=-Marken in markers.csv — nichts zuzuordnen.")
|
||||
|
||||
ende = max(pings[-1][0], sec(rows[-1]["ts_local"]) if rows else 0) + 1
|
||||
fenster = [(r[1], rooms[i + 1][1] if i + 1 < len(rooms) else ende, r[2])
|
||||
for i, r in enumerate(rooms)]
|
||||
|
||||
print("%-16s %6s %-24s %-18s %s" % ("Raum", "Dauer", "Signal min/avg/max", "Verlust", "längste Lücke"))
|
||||
print("-" * 92)
|
||||
for start, stop, name in fenster:
|
||||
p = [x for x in pings if start <= x[0] < stop]
|
||||
r = [x for x in rows if start <= sec(x["ts_local"]) < stop]
|
||||
if not p and not r:
|
||||
continue
|
||||
# Verlust und laengste Luecke aus den Sequenznummern
|
||||
luecke, verloren = 0.0, 0
|
||||
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)
|
||||
gesamt = (p[-1][1] - p[0][1] + 1) if len(p) > 1 else 0
|
||||
sig = [int(x["sta_signal"]) for x in r]
|
||||
ifaces = sorted({AP.get(x["sta_iface"], x["sta_iface"]) for x in r if x["sta_iface"]})
|
||||
print("%-16s %5ds %-24s %-18s %s" % (
|
||||
name, stop - start,
|
||||
"%d / %.1f / %d dBm" % (min(sig), sum(sig) / len(sig), max(sig)) if sig else "—",
|
||||
"%d von %d (%.0f %%)" % (verloren, gesamt, 100 * verloren / gesamt) if gesamt else "—",
|
||||
("%.1f s" % luecke) if luecke else "keine"))
|
||||
if ifaces:
|
||||
print("%-16s auf %s" % ("", ", ".join(ifaces)))
|
||||
print("\nLesehilfe: eine längste Lücke über ~2 s ist für einen Videocall eine hörbare Unterbrechung,")
|
||||
print("über 10 s ist der Call praktisch tot. Signal unter −70 dBm ist die Zone, in der das passiert.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user