diff --git a/docs/tp25-runde-20260822-auswertung.md b/docs/tp25-runde-20260822-auswertung.md index 3223203..7a3b101 100644 --- a/docs/tp25-runde-20260822-auswertung.md +++ b/docs/tp25-runde-20260822-auswertung.md @@ -129,7 +129,7 @@ Entscheidung — und sie steht unter dem Vorbehalt, dass 20 MHz vorher gemessen --- -## Zwei Werkzeugmängel, die dieser Lauf aufgedeckt hat +## Zwei Werkzeugmängel, die dieser Lauf aufgedeckt hat — ✅ beide behoben (2026-08-22) 1. 🔴 **Es gibt keinen Stopp-Knopf.** Christoph drückte 22:53:26 „📴 Call beendet" und „⛔ Last sofort stoppen" — beides schreibt nur Marken (`ABORT_killed=0`, es gab keine @@ -140,3 +140,14 @@ Entscheidung — und sie steht unter dem Vorbehalt, dass 20 MHz vorher gemessen Telefone, hält die Zahlen aber nur im Speicher für die Anzeige. Nachträglich ließ sich nur das *Roaming-Log* am Manager retten (`manager-log.txt`) — Ereignisse, keine Zeitreihe. Deshalb fehlt oben der direkte Terrassen-Vergleich. + +**Behoben.** Die Konsole hat jetzt einen echten `⏹ Aufzeichnung stoppen`, der die Messung +beendet (`stop.flag`), und es werden **beide** Telefone gepingt (`walk-ping.csv` + +`walk-ping-iphone.csv`) sowie beide im 2-s-Takt mitgeschrieben (`beide-telefone.csv`). +Beim Testen fielen dabei zwei weitere Fehler auf, die beide diesen Lauf betroffen hätten: +`PINGER=$(ping_lauf …)` blockierte (Kommandosubstitution wartet auf die stdout-Pipe, die der +Hintergrundjob offen hält) — der zweite Ping wäre nie gestartet; und `kill $SAMPLER` traf nur +den Wrapper, dessen Subshell samt SSH-Sitzung verwaist weiterlief. Jetzt wird der +Prozessbaum kindzuerst beendet. + +⇒ **Der nächste Lauf beantwortet die Terrassen-Frage.** Diesmal nicht. diff --git a/docs/tp25-runde.sh b/docs/tp25-runde.sh index ddb8ecb..70f5956 100755 --- a/docs/tp25-runde.sh +++ b/docs/tp25-runde.sh @@ -57,7 +57,10 @@ aufraeumen() { echo "Runde beendet. Aufraeumen …" bash docs/tp25-pin-s22.sh off >/dev/null 2>&1 && echo " AP-Pin geloest." [[ -n "$KONSOLE" ]] && kill "$KONSOLE" 2>/dev/null && echo " Konsole beendet." - pkill -f 'sshpass -e ssh.*10.40.99.1' 2>/dev/null + # Muster bewusst ohne -e/-p: es gab beide Varianten, und die enge Fassung liess den + # Sampler stehen. + pkill -f 'sshpass.*10.40.99.1' 2>/dev/null + pkill -f 'tp6-monitor-core.sh' 2>/dev/null echo echo "Auswertung:" echo " python3 docs/tp25-gehweg-auswertung.py --dir $TP6_RUN_DIR" diff --git a/docs/tp25-walk-messung.sh b/docs/tp25-walk-messung.sh index de72364..e98c193 100755 --- a/docs/tp25-walk-messung.sh +++ b/docs/tp25-walk-messung.sh @@ -19,8 +19,14 @@ set -uo pipefail RUN_DIR="${TP6_RUN_DIR:-/private/tmp/claude-501/-Users-christophemsenhuber-dev-bf-network-r6/0d86ebd1-035f-4513-a231-5cca8111ecfd/scratchpad/tp6-run2}" -PHONE="${TP25_PHONE_IP:-10.40.10.196}" -PHONE_MAC="${TP25_PHONE_MAC:-F2:62:4C:E1:AF:09}" +PHONE="${TP25_PHONE_IP:-10.40.10.178}" # S22 -- wird gepingt und gesampelt +PHONE_MAC="${TP25_PHONE_MAC:-2A:0D:B0:5D:AD:A7}" +# 🔴 Seit dem Lauf vom 2026-08-22 wird das ZWEITE Telefon mitgepingt. Vorher lief der Ping +# nur auf das gepinnte Geraet -- und weil das per Automatik immer auf dem GEGEN-AP sitzt, +# fehlte ausgerechnet die Strecke, die die Alltagsabdeckung zeigt. Der Terrassen-Vergleich +# liess sich deshalb nicht fuehren, obwohl beide Telefone dabei waren. +PHONE2="${TP25_PHONE2_IP:-10.40.10.196}" # iPhone -- roamt frei +PHONE2_NAME="${TP25_PHONE2_NAME:-iphone}" STATE="$RUN_DIR/phase.json" MARKERS="$RUN_DIR/markers.csv" HERE="$(cd "$(dirname "$0")/.." && pwd)" @@ -72,7 +78,7 @@ state "Kein Call nötig. 🚶 drücken, wenn du losgehst, 📍 wen bash "$HERE/docs/tp6-monitor-core.sh" --out "$RUN_DIR/sampler.csv" --mac "$PHONE_MAC" --interval 1 \ > "$RUN_DIR/sampler.out" 2>&1 & SAMPLER=$! -# 5 Pings/s auf das Telefon — GEPINGT WIRD VOM CORE, nicht von diesem Rechner. +# 5 Pings/s auf JEDES Telefon — GEPINGT WIRD VOM CORE, nicht von diesem Rechner. # 🔴 Der Grund: pingt der Laptop, laeuft der Weg ueber ZWEI Funkstrecken (Laptop -> AP -> # Core -> AP -> Telefon). Fehlt dann ein Paket, ist nicht entscheidbar, welches der # beiden Geraete weg war. Vom Core aus ist die Strecke bis zum AP durchgehend Kabel, @@ -80,35 +86,84 @@ bash "$HERE/docs/tp6-monitor-core.sh" --out "$RUN_DIR/sampler.csv" --mac "$PHONE # (Aufgefallen am 2026-08-21, als das MacBook waehrend einer Messung vom Dock ans # WLAN wechselte und dabei auch noch die IP wechselte.) PW="$(grep -m1 '^CRS326_CORE_PASS=' ~/.secrets/credentials.env | cut -d= -f2- | sed 's/^["'"'"'"'"'"']//; s/["'"'"'"'"'"']$//')" -echo "ts_local,seq,ms" > "$RUN_DIR/walk-ping.csv" -( - while true; do - sshpass -p "$PW" ssh -o StrictHostKeyChecking=no -o ConnectTimeout=8 \ - -o ServerAliveInterval=15 admin@10.40.99.1 \ - "/ping $PHONE interval=200ms count=100000" 2>/dev/null \ - | while IFS= read -r l; do - l="${l%$'\r'}" - case "$l" in - *SEQ*HOST*) continue ;; - esac - seq="$(printf '%s' "$l" | awk '{print $1}')" - case "$seq" in - ''|*[!0-9]*) continue ;; - esac - case "$l" in - *timeout*) printf '%s,%s,\n' "$(date +%H:%M:%S)" "$seq" ;; - *) printf '%s,%s,%s\n' "$(date +%H:%M:%S)" "$seq" \ - "$(printf '%s' "$l" | awk '{print $NF}')" ;; - esac - done >> "$RUN_DIR/walk-ping.csv" - printf '%s,,RECONNECT\n' "$(date +%H:%M:%S)" >> "$RUN_DIR/walk-ping.csv" - sleep 2 - done -) & PINGER=$! -trap 'kill $SAMPLER $PINGER 2>/dev/null; pkill -f "ping $PHONE interval=200ms" 2>/dev/null; state "Aufzeichnung gestoppt."; echo; echo "Gestoppt."' EXIT +# $1 = Ziel-IP, $2 = Ausgabedatei. Schreibt "ts_local,seq,ms"; leeres ms = Verlust. +# 🔴 sshpass -e statt -p: mit -p steht das Router-Passwort in der Kommandozeile und damit +# in `ps` und in jedem Prozess-Trace. Am 2026-08-22 ist es genau so im Klartext in einem +# Sitzungsmitschnitt gelandet. +ping_lauf() { + local ziel="$1" datei="$2" + echo "ts_local,seq,ms" > "$datei" + ( + while true; do + SSHPASS="$PW" sshpass -e ssh -o StrictHostKeyChecking=no -o ConnectTimeout=8 \ + -o ServerAliveInterval=15 admin@10.40.99.1 \ + "/ping $ziel interval=200ms count=100000" 2>/dev/null \ + | while IFS= read -r l; do + l="${l%$'\r'}" + case "$l" in + *SEQ*HOST*) continue ;; + esac + seq="$(printf '%s' "$l" | awk '{print $1}')" + case "$seq" in + ''|*[!0-9]*) continue ;; + esac + case "$l" in + *timeout*) printf '%s,%s,\n' "$(date +%H:%M:%S)" "$seq" ;; + *) printf '%s,%s,%s\n' "$(date +%H:%M:%S)" "$seq" \ + "$(printf '%s' "$l" | awk '{print $NF}')" ;; + esac + done >> "$datei" + printf '%s,,RECONNECT\n' "$(date +%H:%M:%S)" >> "$datei" + sleep 2 + done + ) & +} + +# 🔴 Die PID NICHT ueber $(ping_lauf ...) holen: die Kommandosubstitution wartet, bis die +# stdout-Pipe schliesst, und der Hintergrundjob darin haelt sie offen -- das Skript blieb +# dort stehen, der zweite Ping wurde nie gestartet (2026-08-22). Die Funktion laeuft in +# der aktuellen Shell, also liefert $! danach die richtige PID. +ping_lauf "$PHONE" "$RUN_DIR/walk-ping.csv"; PINGER=$! +ping_lauf "$PHONE2" "$RUN_DIR/walk-ping-$PHONE2_NAME.csv"; PINGER2=$! + +# 🔴 Kinder ZUERST. `kill $SAMPLER` traf nur den Wrapper; dessen Subshell samt SSH-Sitzung +# wurde dabei verwaist und lief weiter -- nach dem Stopp hielt der Core stumm eine +# Sampler-Sitzung offen (2026-08-22, beim Test des Stopp-Knopfs gefunden). Wer den Vater +# zuerst killt, verliert den Zugriff auf die Kinder. +killbaum() { + local pid="$1" kind + for kind in $(pgrep -P "$pid" 2>/dev/null); do killbaum "$kind"; done + kill "$pid" 2>/dev/null +} + +beenden() { + for pid in $SAMPLER $PINGER $PINGER2; do killbaum "$pid"; done + pkill -f "ping $PHONE interval=200ms" 2>/dev/null + pkill -f "ping $PHONE2 interval=200ms" 2>/dev/null + # Der /ping am Router endet mit der SSH-Sitzung (am 2026-08-22 nachgeprueft: nach dem + # Kill der Sitzungen lief am Core nichts weiter). Bewusst KEIN "job remove" am Geraet -- + # ein pauschales Aufraeumen fremder Jobs waere ein zu grobes Werkzeug fuer ein Messskript. + state "Aufzeichnung gestoppt." + echo; echo "Gestoppt. Auswertung:" + echo " python3 docs/tp25-gehweg-auswertung.py --dir $RUN_DIR" +} +trap beenden EXIT echo "Gehweg-Messung laeuft." -echo " Funk -> $RUN_DIR/sampler.csv" -echo " Ping -> $RUN_DIR/walk-ping.csv (Quelle: crs326-core)" -while true; do touch "$STATE" 2>/dev/null; sleep 5; done +echo " Funk -> $RUN_DIR/sampler.csv" +echo " Ping -> $RUN_DIR/walk-ping.csv ($PHONE, gepinnt)" +echo " Ping -> $RUN_DIR/walk-ping-$PHONE2_NAME.csv ($PHONE2, frei roamend)" +echo " Stoppen: '⏹ Aufzeichnung stoppen' auf der Konsole -- oder Ctrl-C hier." + +# 🔴 Bis 2026-08-22 lief hier nur `while true; do touch; sleep 5; done`, und das Skript war +# ausschliesslich per Ctrl-C zu beenden -- obwohl der Anleitungstext auf der Konsole einen +# Stopp-Knopf versprach. Christoph drueckte "Call beendet" und den Not-Aus, beides schrieb +# nur Marken, und die Messung lief weiter. Jetzt gibt es den Knopf wirklich: die Konsole +# legt stop.flag ab, und diese Schleife bricht darauf ab. +rm -f "$RUN_DIR/stop.flag" +while [[ ! -f "$RUN_DIR/stop.flag" ]]; do + touch "$STATE" 2>/dev/null + sleep 2 +done +echo "Stopp von der Konsole empfangen." diff --git a/docs/tp6-marker/server.py b/docs/tp6-marker/server.py index dd4049d..0b71806 100644 --- a/docs/tp6-marker/server.py +++ b/docs/tp6-marker/server.py @@ -283,6 +283,12 @@ def beobachter(): continue _live.update(iphone=teile[0], iphone_sig=teile[1], s22=teile[2], s22_sig=teile[3], stand=time.time()) + # 🔴 Bis 2026-08-22 blieben diese Zahlen NUR im Speicher, fuer die Anzeige. + # Nach dem Rundgang fehlte deshalb die halbe Messung: das S22 sitzt per + # Automatik immer auf dem Gegen-AP, die Alltagsabdeckung zeigt das iPhone + # -- und genau dessen Zeitreihe war weg. Nachtraeglich liess sich nur das + # Roaming-Log am Manager retten: Ereignisse, keine Kurve. + mitschreiben(teile) entscheide() except Exception as e: # Ein Beobachter, der still stirbt, ist schlimmer als keiner (Lehre aus dem @@ -295,6 +301,19 @@ def beobachter(): time.sleep(3) +def mitschreiben(teile): + """Beide Telefone im 2-s-Takt in den Lauf schreiben: iface + Signal je Geraet.""" + try: + pfad = _p("beide-telefone.csv") + frisch = not os.path.exists(pfad) or os.path.getsize(pfad) == 0 + with open(pfad, "a") as fh: + if frisch: + fh.write("ts_epoch,ts_local,iphone_iface,iphone_sig,s22_iface,s22_sig\n") + fh.write("%d,%s,%s\n" % (time.time(), time.strftime("%H:%M:%S"), ",".join(teile))) + except Exception: + pass + + def entscheide(): """Gegen-AP setzen, sobald das iPhone den AP wechselt.""" if not _auto["an"] or _pin["busy"]: @@ -510,6 +529,14 @@ def render(st): notaus = ('
' ) if st.get("driver") else "" + # 🔴 Der Stopp-Knopf. Er beendet die AUFZEICHNUNG (stop.flag -> tp25-walk-messung.sh), + # nicht nur eine Marke. Vorher gab es ihn nicht, obwohl der Anleitungstext ihn nannte: + # am 2026-08-22 drueckte Christoph "Call beendet" und den Not-Aus, beide schrieben nur + # Marken, und der Core pingte danach noch minutenlang weiter. + if st.get("driver"): + notaus = ('' + ) + notaus # 🚶 Gehen/Stehen braucht keinen Treiber -- das sind reine Zeitmarken fuer die Auswertung. btns = ('