commit 65eb7a9dd5eb8a674e2108f1cee140300ee35374 Author: Poulpe Date: Mon Apr 27 12:53:19 2026 +0000 Initial archive: ping-pong-ping demo (USBL DS-TWR + SS-TWR + passive OWR) Self-contained HTML demo with two interactive canvas animations: - Animation 1: DS-TWR principle (mobile-mobile, both sides know distance) - Animation 2: USV master + N AUVs, SS-TWR active + passive OWR for non-target listeners Realistic AUV operating params (1-2 kt cruise, 5-700 m range). Includes README documenting the protocol, design decisions taken during the 2026-04-27 design conversation between Flag and Poulpe, deployment routing (Gateway Caddy + Core Caddy path-based), and TODO for future iterations. Live: https://laboratoire.freeboxos.fr/ping-pong-ping/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..b8ddcfc --- /dev/null +++ b/README.md @@ -0,0 +1,104 @@ +# Ping-Pong-Ping + +Interactive HTML demo + reference notes for **acoustic distance ranging** between USBL nodes (USV master ↔ AUV slaves). Built collaboratively between Flag and Poulpe on 2026-04-27 from a single design conversation. + +**Live demo** : https://laboratoire.freeboxos.fr/ping-pong-ping/ + +--- + +## What this archives + +A self-contained `index.html` (no framework, no external assets) that explains and animates two acoustic ranging schemes used in underwater positioning: + +1. **DS-TWR** (Double-Sided Two-Way Ranging) — symmetric ping/pong/ping where **both** sides learn the distance without clock synchronization. Used when the two nodes are peers (mobile-mobile, swarm coopératif). +2. **SS-TWR + passive OWR** (Single-Sided Two-Way Ranging + One-Way Ranging by passive listeners) — asymmetric scheme used when **only the master needs the distance**, with the bonus that any other AUV in acoustic range can compute its own distance to the master from the same single ping. + +Both animations run continuously, with realistic AUV operating parameters (1–2 kt cruise speed, 5–700 m range, c = 1500 m/s). + +## The protocol — short version + +### DS-TWR (animation 1) +``` +A → B : ping1 +B → A : pong (B encodes turnaround_B in the pong frame) +A → B : ping2 (A encodes turnaround_A in the ping2 frame) + +A side : TOF = (round_trip_A − turnaround_B) / 2 +B side : TOF = (round_trip_B − turnaround_A) / 2 +distance = TOF · c +``` +No clock sync required. Each side measures only local deltas. + +### SS-TWR + passive OWR (animation 2) +``` +USV → AUVi (addressed) : ping (frame carries target ID 0xAi + send timestamp) + + AUVi receives → match ID → responds: + AUVi → USV : pong (encodes turnaround_AUVi) + + All other AUVj receive the same ping (medium is shared): + AUVj computes distance passively : (T_recv_local − T_send_USV) · c + +USV side (active TWR for AUVi) : TOF = (round_trip − turnaround_AUVi) / 2 +AUVj side (passive OWR) : TOF = T_recv_local − T_send_USV +``` +**One ping → N range fixes.** The medium remains free between cycles. + +### Required infrastructure for OWR +The passive AUVs must share a **time reference** with the USV. Two practical options: + +- **GPS-disciplined oscillators** synchronized at the surface before mission launch, with bounded drift (typically < 1 µs over a few hours for OCXO-grade clocks). +- **Send-timestamp embedded in the ping frame** : the USV writes its clock value `T_send` in the ping payload, every receiver reads it and compares to its own `T_recv`. Subject to the same clock-drift caveat. + +DS-TWR (animation 1) does **not** need this — that's its main feature. SS-TWR + passive OWR (animation 2) trades the synchronization requirement for much faster scan rates. + +## Design decisions taken in the conversation + +These reflect the iterative refinement during the conversation, summarized so the rationale isn't lost: + +| Decision | Why | +|---|---| +| **Two animations, not three** | Initially the multi-AUV section had a Broadcast (BC-DS-TWR) mode with N pongs in staggered slots. Flag judged it "relou" — addressed sequential is conceptually simpler, no slot collisions, and combined with passive OWR it's faster anyway. Dropped. | +| **SS-TWR (not DS-TWR) for multi-AUV** | In the iUSBL setup (USV tracks AUVs), only the USV needs the distance. The third leg (ping2) is dead weight. Switched the multi-AUV animation to SS-TWR, kept DS-TWR for the principle demo where the symmetry matters. | +| **Passive OWR for non-target AUVs** | Originally non-target AUVs were drawn as "✗ ignore". Flag pointed out they can use the ping passively. Each ping now produces N range fixes (1 active + N−1 passive). | +| **Realistic AUV speeds (1–2 kt) and ranges (5–700 m)** | First version had AUVs moving at ~140 kt over 5 km — physically impossible. Recalibrated to typical coastal AUV ops. | +| **No DS-TWR-vs-SS-TWR speedup table** | Removed cycle-time comparison readouts to avoid clutter. The "fluidity" is visible directly. | +| **No ID match alarm** | Replaced "✗ ignore" red alert with "📡 passive OWR" cyan badge, since non-target AUVs are useful, not ignored. | + +## File structure + +``` +ping-pong-ping/ +├── index.html # self-contained demo (HTML + CSS + JS, no externals) +├── README.md # this file +└── deploy.sh # one-shot push to Core Caddy webroot +``` + +`index.html` contains two `` animations driven by independent IIFEs, prose sections explaining each scheme, and worked numerical examples. + +## Deploy + +The page is served from Core (`192.168.0.82`) via Caddy on the path `laboratoire.freeboxos.fr/ping-pong-ping/`. + +Routing layers : + +- **Core Caddyfile** (`/docker/caddy/Caddyfile` on `.82`) : `handle /ping-pong-ping { redir /ping-pong-ping/ 307 }` + `handle_path /ping-pong-ping/* { root * /srv/www/ping-pong-ping; file_server }` +- **Gateway Caddyfile** (`/etc/caddy/Caddyfile` on LXC 103, `.128`) : `handle /ping-pong-ping/* { reverse_proxy 192.168.0.82:80 }` — note `handle` (not `handle_path`) to **preserve the prefix** when forwarding to Core's path-based block. Otherwise Core sees `/` and falls into the catchall. + +To re-deploy after an edit : + +```bash +./deploy.sh +``` + +## TODO / future work + +- [ ] Add a 3rd animation showing **2D positioning** — currently distance only is animated. AUVs equipped with USBL micro-arrays could also compute their bearing-to-USV; the page mentions this in passing but doesn't visualize it. +- [ ] Show **clock drift visualization** in the OWR animation (the synchronization assumption is the load-bearing piece — worth illustrating). +- [ ] **Compare with the real Kogger USBL frames** decoded in `floppyrj45/cosma-nav/extract/decode_kogger.py` (8 KB, K-Link `BB 55` protocol reverse-engineered). The 2026-04-08 La Ciotat dataset contained only setup/config frames, no acoustic fixes — when a dataset with real position responses lands, plug the numbers into this demo for end-to-end validation. +- [ ] Add a **noise model** (USBL inherent precision is 0.5–2 % of slant range) so the plot dots scatter realistically around the truth curves. +- [ ] **Multipath / NLOS** scenario : show what happens when an AUV's pong takes a bounced path. Worth its own panel. + +## Credits + +Conversation between Flag (concept, design feedback, iterative steering) and Poulpe (implementation, deployment, prose) on 2026-04-27 in Discord `#ping-pong-ping`. Archived for future reference and iteration. diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 0000000..0125f89 --- /dev/null +++ b/deploy.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +# Deploy ping-pong-ping/index.html to Core Caddy webroot. +# Idempotent. Requires SSH access to Core (.82) as floppyrj45 + sudo. +set -euo pipefail + +LOCAL="$(dirname "$0")/index.html" +REMOTE_HOST="floppyrj45@192.168.0.82" +REMOTE_TMP="/tmp/ping-pong-ping.html" +REMOTE_TARGET="/docker/caddy/www/ping-pong-ping/index.html" + +scp "$LOCAL" "$REMOTE_HOST:$REMOTE_TMP" +ssh "$REMOTE_HOST" "sudo mv $REMOTE_TMP $REMOTE_TARGET && sudo chown 1000:1000 $REMOTE_TARGET" + +echo "Deployed. Public URL: https://laboratoire.freeboxos.fr/ping-pong-ping/" +curl -skLo /dev/null -w 'HTTP %{http_code}, %{size_download} bytes\n' \ + https://laboratoire.freeboxos.fr/ping-pong-ping/ diff --git a/index.html b/index.html new file mode 100644 index 0000000..d23dcfc --- /dev/null +++ b/index.html @@ -0,0 +1,1116 @@ + + + + + +Ping-Pong-Ping — Mesure symétrique de distance USBL + + + +
+ +

Ping-Pong-Ping

+

Mesure symétrique de distance par échange acoustique 3-way — DS-TWR (Double-Sided Two-Way Ranging)

+ +

Le problème

+

+En USBL classique, deux modes coexistent pour mesurer la distance entre deux transducteurs immergés : +

+
    +
  • Horloges pré-synchronisées — chaque appareil horodate l'émission et la réception. Précis mais nécessite une synchro fine (drift d'horloge à compenser).
  • +
  • Mode transpondeur — le maître envoie un ping, l'esclave répond. Le maître mesure le temps aller-retour. Seul le maître connaît la distance.
  • +
+

+Si les deux appareils doivent connaître la distance simultanément (ex : deux véhicules autonomes coopératifs, swarm BlueROV, balise mobile/mobile), le mode transpondeur classique est asymétrique. +

+ +

Le principe — 3 transmissions, 2 mesures identiques

+

+On échange trois messages au lieu de deux. Chaque appareil mesure son propre turnaround local (le temps entre réception d'un message et émission du suivant), puis l'encode dans la trame renvoyée. Les deux côtés peuvent alors retirer ce délai du round-trip qu'ils observent → ils calculent le même TOF (Time of Flight) sans avoir jamais synchronisé leurs horloges. +

+ +
+

Séquence

+ + + + + +
#ÉmetteurContenuMesure côté récepteur
1ABPing (id session)B note T1B
2BAPong + turnaround_BA note T2A, calcule TOF
3ABPing2 + turnaround_AB note T3B, calcule TOF
+
+ +

Formule

+
+turnaround_B = T_send_pong_B  − T_recv_ping1_B    (mesuré par B, encodé dans pong)
+turnaround_A = T_send_ping2_A − T_recv_pong_A     (mesuré par A, encodé dans ping2)
+
+round_trip_A = T_recv_pong_A   − T_send_ping1_A   (mesuré par A localement)
+round_trip_B = T_recv_ping2_B  − T_send_pong_B    (mesuré par B localement)
+
+TOF_A = (round_trip_A − turnaround_B) / 2
+TOF_B = (round_trip_B − turnaround_A) / 2
+
+distance = TOF · c    avec  c ≈ 1500 m/s en eau de mer
+
+ +

Animation continue — B mobile

+

B oscille en distance par rapport à A (fixe). Les cycles ping-pong-ping s'enchaînent en boucle ; chaque cycle complet ajoute un point de mesure au plot. La distance vraie (rouge pointillé) est comparée aux distances mesurées côté A (cyan) et côté B (orange).

+ +
+
+ + + + + +
+ +
+ USBL A (fixe) + USBL B (mobile) + ping (A→B) + pong (B→A) +
+ +
+ Distance vraie + Mesure côté A (TOF_A · c) + Mesure côté B (TOF_B · c) +
+ +
+
+
temps sim
0.00 s
+
distance vraie
— m
+
cycles complets
0
+
turnaround A / B
80 / 50 ms
+
+
+
dernière mesure A
— m
+
dernière mesure B
— m
+
erreur A (vs vraie au départ)
— m
+
erreur B (vs vraie au départ)
— m
+
+
+
+ +

Ce qu'il faut observer

+
    +
  • Concordance A et B — les deux mesures suivent la courbe vraie. C'est le bénéfice principal du 3-way : deux observateurs, une seule vérité.
  • +
  • Petit retard de phase — chaque mesure est ancrée au début de cycle (instant du ping1). Quand B se déplace vite, la mesure publiée correspond à la position de B il y a ~3·TOF + turnarounds secondes (jusqu'à 6 s à 3 km). Visible quand la période d'oscillation est courte.
  • +
  • Biais lié au mouvement de B : négligeable en pratique — pour un AUV à 1 kt (≈ 0,5 m/s) et c = 1500 m/s, B se déplace ~1 m pendant un TOF de 2 s à 3 km. Très en-dessous du bruit USBL natif (typiquement 0,5–2 % de la slant range, soit 15–60 m à 3 km). Même à 5 kt c'est ~5 m, encore noyé dans le bruit. Donc en ops AUV typiques (1–3 kt), oublier ce biais ; il ne devient significatif qu'au-delà de ~10 kt sur des cibles très éloignées.
  • +
+ +

Multi-AUV — adressage séquentiel + écoute passive

+

Cas d'école standard : un USV de surface (master, équipé GPS) interroge ses AUVs en plongée (slaves) un par un. L'USV envoie un ping ciblé sur l'ID d'un AUV ; seul l'AUV adressé répond avec son pong. Pas de ping2 : seul l'USV a besoin de la distance (il pilote la trilatération depuis la surface), donc SS-TWR suffit.

+

Bonus : écoute passive (OWR). Le médium est partagé — quand l'USV émet le ping, tous les AUVs l'entendent, pas que la cible. Les AUVs non adressés ne répondent pas (silence radio), mais ils peuvent extraire leur propre TOF à condition de partager une référence temporelle avec l'USV (horloges disciplinées GPS / AOA pré-mission, ou ping qui embarque T_send dans sa trame). Chacun calcule : distance = (T_recv_local − T_send_USV) · c. C'est de la one-way ranging (OWR), comme GPS.

+

Conséquence : 1 ping → N mesures de distance USV↔AUV (1 active TWR pour la cible, N−1 passives OWR pour les autres). Le médium reste libre, et l'USV peut maintenir un fix sur tout le swarm en parallèle. Visible dans l'anim : tous les AUVs affichent 📡 passive OWR ou ✓ active TWR à chaque cycle, et tous les points apparaissent dans le plot.

+ +
+
+ + + + + 4 +
+ +
+ +
+
temps sim
0.00 s
+
phase courante
+
cycles complets
0
+
durée d'un cycle (1 AUV)
— s
+
durée scan complet (N AUVs)
— s
+
+
+
+ +

Exemple chiffré (cas statique)

+

Distance vraie : 3000 m. Célérité : 1500 m/s → TOF unidirectionnel = 2.000 s.

+
+A envoie ping1     à T_A = 0.000 s
+B reçoit ping1     à T_B = 2.000 s   (TOF = 2 s, mais B ignore T_A)
+B envoie pong      à T_B = 2.050 s   → turnaround_B = 50 ms encodé dans pong
+A reçoit pong      à T_A = 4.050 s
+   → round_trip_A = 4.050 s
+   → TOF_A = (4.050 − 0.050) / 2 = 2.000 s ✓
+   → distance = 2.000 × 1500 = 3000 m ✓
+
+A envoie ping2     à T_A = 4.130 s   → turnaround_A = 80 ms encodé dans ping2
+B reçoit ping2     à T_B = 6.130 s
+   → round_trip_B = 6.130 − 2.050 = 4.080 s
+   → TOF_B = (4.080 − 0.080) / 2 = 2.000 s ✓
+   → distance = 3000 m ✓
+
+ +

Cas d'usage

+
    +
  • Swarm sous-marin — flottille d'AUV / BlueROV qui doivent maintenir une formation. Chacun connaît sa distance aux voisins en parallèle.
  • +
  • Mobile-mobile — deux plongeurs ou deux balises mobiles, sans station fixe maître.
  • +
  • Redondance / cross-check — deux mesures TOF indépendantes (côté A et côté B) doivent être identiques. Un écart trahit un bruit ou un multipath.
  • +
  • Synchro douce — l'échange permet aussi d'estimer le drift relatif des horloges et d'éviter une vraie synchro hardware.
  • +
+ +

Limites

+
    +
  • Latence du DS-TWR — 3 transmissions au lieu de 2 = ~50 % de cycle en plus. À 3 km, ~6 s par mesure (DS-TWR symétrique) vs ~4 s pour SS-TWR. Inadapté à un tracking rapide haut débit ; pour du multi-cible, raccourcir la portée ou utiliser SS-TWR si seul un côté a besoin de la distance.
  • +
  • Bande passante — il faut encoder turnaround dans la trame acoustique (typiquement 16 ou 32 bits). Coût négligeable mais non nul.
  • +
  • Drift d'horloge non-linéaire — sur des séquences longues (>10 s) ou des oscillateurs très bas de gamme, l'hypothèse de linéarité casse. DS-TWR le mitige beaucoup mieux que SS-TWR mais ne l'élimine pas.
  • +
  • Pas de TDOA — ce mode donne la distance, pas l'angle. Il complète un USBL (qui mesure l'azimut) mais ne le remplace pas.
  • +
+ +

Référence terrestre — DecaWave UWB

+

+Ce schéma est exactement celui qu'utilisent les puces UWB DecaWave DW1000 / DW3000 (Apple AirTag, Qorvo, etc.) sous le nom DS-TWR. La transposition acoustique change uniquement l'échelle : célérité 1500 m/s vs 3·10⁸ m/s, et turnaround mesuré en ms plutôt qu'en ns. La logique de calcul est identique. +

+ +
Poulpe 🐙 — pour Flag — 2026-04-27
+
+ + + + + +