Initial: ContinuousTransponder wrapper for Kogger USBL

High-level Python wrapper around the upstream cosma-tech/kogger_acousticAntenna
driver. Configures a Kogger acoustic antenna as a permanent slave transponder
in a single start() call: address filter, echo filter, optional TDMA sync slot,
permanent response window, and Python callbacks for each ping received.

No modification to the upstream driver — only composes existing public methods
in the right order. Snapshot of upstream driver included read-only under driver/
for reference.

Includes:
- transponder_continu.py (302 lines): the wrapper class + CLI
- examples/auv_slave.py (79 lines): usage example with logging
- README.md: design rationale, usage, multi-AUV TDMA, watchdog, hardware wiring
- driver/: snapshot of cosma-tech/kogger_acousticAntenna at commit 1b539f9
  ('Add index slot for multi pinger', 2025-03-11)

Built for Cosma context (USV master + N AUVs slaves) following the design
conversation in Discord #ping-pong-ping (2026-04-27). See poulpe/ping-pong-ping
on Gitea for the interactive demo of the protocol.
This commit is contained in:
2026-04-27 22:08:44 +00:00
commit 9a158f5c5f
53 changed files with 7894 additions and 0 deletions

143
README.md Normal file
View File

@@ -0,0 +1,143 @@
# Kogger USBL — mode transpondeur continu
Wrapper Python de haut niveau autour du driver Kogger `kogger_acousticAntenna` (cosma-tech) qui configure une antenne acoustique en **slave transpondeur permanent** : elle écoute en continu les pings adressés à son ID, répond automatiquement, ignore les autres, et expose un callback Python pour chaque ping reçu.
**Pas de re-armement par ping. Une seule init. Le device répond tant que le process tourne.**
Construit pour le contexte Cosma (USV master + N AUVs slaves), suite à la conversation Discord [#ping-pong-ping](https://gitea.nowyouknow.fr/poulpe/ping-pong-ping) du 2026-04-27 sur les schémas SS-TWR + écoute passive OWR.
---
## Pourquoi ce wrapper
Le driver Kogger upstream (`kogger_protocol_driver.KoggerSBPDevice`) expose toutes les commandes bas niveau nécessaires pour le mode transpondeur :
| Commande | Rôle |
|---|---|
| `set_usbl_transponder(enable)` | Ouvre/ferme la fenêtre de réponse acoustique. `True` = always (timeout `0xFFFFFFFF` µs). |
| `set_usbl_request_address_filter([...])` | Liste 8 adresses (0-7 ou `0xFF`=désactivé) pour lesquelles le device doit répondre. |
| `set_usbl_monitor_config(enable, echo_filter_response_us, echo_filter_request_us)` | Active la surveillance + le filtre d'écho (évite l'auto-déclenchement par réverbération). |
| `set_sync_mode(slot_total, slot_index, slot_duration, enable_delay)` | Enrôle le device dans un slot TDMA (utile en multi-slave). |
| `register_callback(ID_USBL_SOLUTION, fn)` | Branche un callback Python sur les frames USBL solution reçues. |
Mais il faut les composer dans **le bon ordre**, gérer les callbacks, et idéalement avoir un watchdog qui ré-applique la config si le device a redémarré ou que le master a été silencieux trop longtemps.
C'est exactement ce que `ContinuousTransponder` fait — un appel `start()` configure tout et le device entre en mode permanent.
## Architecture
```
.
├── README.md # ce fichier
├── transponder_continu.py # ★ wrapper haut niveau (zéro modif du driver)
├── examples/
│ └── auv_slave.py # exemple AUV slave avec logging
└── driver/ # snapshot read-only du driver upstream
├── kogger_protocol_driver.py (1509 lignes, 78 KB)
├── communication.py
├── interface.py
├── simulation_kogger.py
├── README.md
└── ...
```
**Aucune modification du driver Kogger d'origine.** Tout passe par les méthodes publiques. Quand cosma-tech publie une nouvelle version du driver, il suffit de remplacer le contenu de `driver/` — ce wrapper continue de fonctionner.
## Usage minimal
```python
from transponder_continu import ContinuousTransponder
t = ContinuousTransponder(port="/dev/ttyUSB0", my_address=2,
vehicle_name="AUV-2")
t.on_ping_received(lambda solution: print("ping →", solution))
t.start()
try:
t.run_forever()
finally:
t.stop()
```
## CLI directement
```bash
python3 transponder_continu.py --port /dev/ttyUSB0 --address 2
```
Options utiles :
```
--vehicle AUV-2 # étiquette pour les logs CSV du driver
--watchdog-s 15 # ré-applique la config si silence > 15 s
--slot-total 4 --slot-index 1 --slot-duration 2.0 # enrôle en TDMA
--echo-filter-us 400000 # 400 ms (défaut)
```
## Contenu de la trame transpondeur
Chaque fois que le master USV envoie un ping ciblé sur l'adresse de cet AUV :
1. Le hardware Kogger filtre par adresse (configuré par `set_usbl_request_address_filter`).
2. Si l'adresse correspond, le hardware **répond automatiquement** (le pong est généré par l'antenne, pas par Python — c'est rapide et déterministe).
3. Le driver émet une frame `ID_USBL_SOLUTION` (0x65) sur la sortie UART contenant la distance hardware-computed, le SNR, l'ID source.
4. Le wrapper appelle votre callback `on_ping_received(message)`.
5. Le state interne (`t.state.last_distance_m`, `last_snr`, `last_ping_id`, `pings_received`) est mis à jour.
Tout cela en boucle, sans intervention.
## Multi-AUV en TDMA
Si N AUVs partagent le canal et que le master scanne en round-robin, configurer chaque slave dans son propre slot évite les collisions de pong :
```python
ContinuousTransponder(
port="/dev/ttyUSB0",
my_address=2,
sync=SyncSlot(slot_total=4, slot_index=1, slot_duration=2.0),
)
```
→ AUV-2 ne répond que pendant son slot (slot_index=1, durée 2 s, cycle complet 8 s).
Convention proposée : `slot_index = my_address - 1`.
## Watchdog
Si on n'a rien reçu depuis `watchdog_timeout_s` secondes (et qu'on a au moins reçu un ping au démarrage), le wrapper ré-applique `set_usbl_transponder(enable=True)`. Couvre le cas où l'antenne Kogger reboot silencieusement et perd sa config.
```python
ContinuousTransponder(..., watchdog_timeout_s=15.0)
```
Off par défaut. Activer en prod si le master est censé pinger en continu.
## Hardware
Câblage Kogger côté antenne (recopié du README upstream pour référence) :
| Couleur | Signal |
|---|---|
| Brown | +V supply |
| Blue | GND |
| Green | UART RX (USBL ← host) |
| Yellow | UART TX (USBL → host) |
| Pink | GND (relié au bleu interne) |
| Gray | TRIGGER_IN |
| White | TRIGGER_OUT |
Connexion typique : `Pi4B → USB-UART converter → antenne Kogger`. Baudrate par défaut 921600.
## Limites / TODO
- [ ] Pas testé sur hardware réel (uniquement statique). Cf. test sur AUV-2 cosma au prochain déploiement.
- [ ] Pas de gestion de reconnexion USB-UART. Si le port disparaît, le process meurt — relancer côté systemd.
- [ ] Pas de support multi-port (un wrapper = une antenne). Pour multi-antennes sur un même Pi, instancier N `ContinuousTransponder` indépendants.
- [ ] L'écoute passive OWR (calcul de distance par non-ciblés à partir de `T_recv T_send`) n'est **pas** dans ce wrapper — elle nécessite une référence temporelle synchronisée USV↔AUVs et l'extraction de `T_send` depuis la trame du ping (pas exposé proprement par le driver upstream pour l'instant). À ajouter quand le besoin se présente.
- [ ] Ajouter un `start()` idempotent (actuellement appeler 2× réenroule les callbacks).
## Références
- Driver upstream : `git@github.com:cosma-tech/kogger_acousticAntenna.git`
- Page démo USBL DS-TWR + SS-TWR + OWR : <https://laboratoire.freeboxos.fr/ping-pong-ping/>
- Repo demo : `poulpe/ping-pong-ping` sur Gitea
- Conversation Discord d'origine : `#ping-pong-ping` (2026-04-27)