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 :

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) ping1 (A→B, plein) pong (B→A, plein) ping2 (A→B, anneau)
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

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

Limites

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.

Implémentation hardware — antenne USBL Kogger

Côté hardware, les antennes acoustiques Kogger sont pilotées en SBP (sync 0xBB 0x55, Fletcher16). Le driver Python (snapshot ici) expose toutes les primitives bas niveau du protocole décrit ci-dessus : set_usbl_request_address_filter (filtre par ID, identique à la sélection adressée animée plus haut), set_usbl_transponder(enable) (ouvre la fenêtre de réponse permanente), set_usbl_monitor_config (filtre d'écho), set_sync_mode (slot TDMA), set_usbl_ping_request_direct (côté master, envoi d'un ping ciblé).

Pour un AUV slave qui doit fonctionner en transpondeur permanent, un wrapper Python poulpe/kogger-transpondeur-continu compose ces appels en une seule init :

from transponder_continu import ContinuousTransponder, SyncSlot

t = ContinuousTransponder(port="/dev/ttyUSB0", my_address=2,
                          sync=SyncSlot(slot_total=4, slot_index=1, slot_duration=2.0),
                          watchdog_timeout_s=15.0)
t.on_ping_received(lambda msg: print(msg))
t.start()
t.run_forever()

L'antenne devient un slave permanent sur l'adresse 2 : elle ignore les pings adressés ailleurs, répond automatiquement aux siens (turnaround géré par le hardware Kogger, donc déterministe), et émet une frame USBL_SOLUTION (0x65) à chaque réception. Côté master USV, le pendant est set_usbl_ping_request_direct(address=2, cmd_id=0).

Sources