Aller au contenu

Lab1 : Acquisition de données

Cette étape se passe pendant une navigation afin d'être dans un environnement maritime pour capter des données.

En mer à bord d'un navire de passagers, nous allons capter des données de position et des données provenant de l'extérieur. Un serveur sur lequel des capteurs sont connectés est également accessible. L'exercice est décomposé en 3 étapes : l'aller, la partie au port de Brest et le retour.

Durant la première partie, nous allons fixer les objectifs de l'exercice, configurer les différents outils et faire des tests de réception et d'analyse. La deuxième partie, au port de Brest, sera constituée de différentes analyses des données reçues, car le nombre de bateaux autour de nous sera plus important. Pendant le retour, la dernière partie de l'exercice, nous utiliserons un serveur de données qui nous donnera la possibilité de réaliser plusieurs manipulations des données reçues.

Matériel

Pour cette étape vous avez besoin d'un PC préférentiellement sous Linux avec une carte Wifi ou une carte réseau filaire. L'ensemble des techniques décrites ci-dessous ont été testées sous Debian 12. Une machine virtuelle vous est fournie avec l'ensemble des outils préinstallés. Nous vous conseillons de l'utiliser pour réaliser ce projet, car nous ne serons pas en mesure d'assister sur l'ensemble des configurations possibles. Néanmoins, vous pouvez installer l'ensemble des outils nécessaires sur un autre Linux (voir liste des outils à installer).

Partie aller

Configuration et tests matériel

Nous utiliserons l'outil AIS-catcher permettant de capturer des trames AIS directement en NMEA183 ( AIS catcher). De nombreux programmes existent sur les git publics qui sont compatibles avec des tuners TNT USB. Ais catcher a l'avantage d'afficher les trames en NMEA183 et également de réaliser un serveur UDP avec les informations reçues.

Vérification du matériel

Dans un terminal, vérifiez que le périphérique est bien reconnu :

utilisateur@dga2025:$ lsusb |grep RTL
Bus 002 Device 002: ID 0bda:2838 Realtek Semiconductor Corp. RTL2838 DVB-T

Désactivation du module TNT

Dans un terminal, désactivez le module TNT du récepteur.

utilisateur@dga2025:$ echo "dvb_usb_rt28xxu" >> /etc/modprobe.d/blacklist.conf

Test de la connexion du récepteur

Dans un terminal, vérifiez que le périphérique est bien reconnu :

utilisateur@dga2025:$ AIS-catcher -l
Résultat attendu
utilisateur@dga2025:~$ AIS-catcher -l
AIS-catcher (build Aug 23 2025) v0.62-153-gecc7f177
(C) Copyright 2021-2025 jvde-github and other contributors
This is free software; see the source for copying conditions.There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Found 1 device(s):
0: Realtek, RTL2838UHIDIR, SN: 00000001

Test de réception

Dans un terminal lancer le programme sans option ni argument pour visualiser les trames AIS :

utilisateur@dga2025:$ AIS-catcher 

Résultat attendu

AIS-catcher (build Aug 23 2025) v0.62-153-gecc7f177
(C) Copyright 2021-2025 jvde-github and other contributors
This is free software; see the source for copying conditions.There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Detached kernel driver
Found Rafael Micro R820T tuner

!AIVDM,1,1,,B,13IJwH0P0ewcJR0Kcv87jgw`25;H,0*2D ( MSG: 1, REPEAT: 0, MMSI: 227983200, timestamp: 20251007122627)
!AIVDM,1,1,,A,13I213gP00OcJP0Kcke91?w`0@O1,0*64 ( MSG: 1, REPEAT: 0, MMSI: 227574030, timestamp: 20251007122628)
!AIVDM,1,1,,B,402:orAvVNanlOcHlhKVsf?005;H,0*69 ( MSG: 4, REPEAT: 0, MMSI: 2275305, timestamp: 20251007122629)
!AIVDM,1,1,,B,B39nia00=orn0sVrfRRF5prQl@O@,0*47 ( MSG: 18, REPEAT: 0, MMSI: 211661220, timestamp: 20251007122630)

À présent vous pouvez connecter un logiciel de navigation sur ce flux pour visualiser les bateaux émettant AIS. Nous allons utiliser le logiciel OpenCPN qui fait partie des logiciels libres les plus utilisés pour l'aide à la navigation. Attention, il n'est pas validé pour être utiliser comme une carte électronique sur un bateau.

Envoi des trames en UDP

Dans un terminal lancer AIS-catcher avec l'option -u localhost et un port de votre choix :

utilisateur@dga2025:$ AIS-catcher -u 127.0.0.1 8888

Ouvrez le logiciel OpenCPN et dans "Outils/Options" créez une nouvelle connexion de type NMEA183 sur localhost et avec le port donné à AIS-catcher. Vérifiez que vous recevez bien un flux AIS s'affichant sur la carte.

Connexion AIS dans OpenCPN

Analyse des trames AIS

AIS-catcher permet d'interpréter les phrases NMEA AIS notamment en JSON. Voici les différentes options d'affichage possibles avec l'option -o :

  • Aucun affichage : -o 0

  • NMEA: -o 1

  • NMEA avec métadonnées : -o 2

  • Interprétation NMEA et résultat en JSON : -o 5

Pour voir la totalité des options possibles, vous pouvez consulter la documentation officielle.

Les différents messages AIS

Parmi les 27 messages AIS possibles, nous allons nous intéresser seulement à certains.

Pour les navires importants et professionnels (émetteurs de type A) les messages 1, 2 et 3 sont intéressants, car ils contiennent des informations de positionnement. Le message 4 est émis par les stations à terre et est donc fréquent pendant les navigations côtières.

L'ensemble des messages avec leur structure est particulièrement bien détaillé sur le site AIS-catcher.

Nous allons nous intéresser uniquement aux trames de type 1, 2, 3, 18, 19.

Lab1.1

Sélection des messages

En utilisant l'utilitaire jq et le format de sortie JSON d'AIS-catcher, affichez uniquement les messages de type 1, 2, 3, 18 et 19.

Affichage du JSON champs par champs

Vous pouvez visualiser plus facilement les champs avec la commande suivante :

     commande |jq .

Sélection des trames
    AIS-catcher -o 5 |jq -c 'select(.type|IN(1,2,3,18,19))'

Affichez les phrases NMEA correspondantes.

Phrases NMEA des messages 1, 2, 3, 18, 19
AIS-catcher -o 5| jq -c 'select(.type|IN(1,2,3,18,19))'|jq -r '.nmea|@csv'

Enregistrement des trames AIS

Nous allons enregistrer les trames AIS qui nous intéressent pour les visualiser dans un logiciel SIG. QGis fait partie des logiciels gratuits les plus utilisés lorsque l'on souhaite réaliser de la visualisation et de l'analyse d'information géographique.

Dans un premier temps il faut enregistrer les trames sous un format interprétable par QGis. Ce logiciel peut recevoir de multiples sources de données. Nous allons utiliser le format CSV qui est le plus simple et surtout le plus facile à contrôler et corriger.

Lab1.2

Export des informations contenues dans les trames AIS

Il est préférable d'enregistrer l'ensemble des trames reçues en JSON puis d'en faire d'autres fichiers par filtrage afin de conserver le flux original. Si d'autres types de trames nous intéressent, nous pourrons toujours utiliser le fichier de base.

Pour commencer enregistrer l'ensemble du flux reçu au format JSON dans le fichier trames.json

Enregistrement du flux AIS

AIS-catcher -u 127.0.0.1 8888 -o 5 > data.json
Nous envoyons le flux NMEA en UDP en même temps pour continuer à avoir accès au flux NMEA.

Nous allons ensuite écrire dans un fichier CSV que nous allons réactualiser toutes les 3 secondes en conservant uniquement le MMSI, la lattitude, la longitude et la puissance du signal.

Création et actualisation d'un fichier CSV
while true; do jq  -c 'select(.type|IN(1,2,3,18,19))' data.json \
|jq -r '[.mmsi,.lat,.lon,.signalpower]|@csv'> data.csv;sleep 3; done

Nous laissons les fichiers se remplir, nous nous en servirons plus tard.

GPS par AIS

Nous n'avons pas de récepteur GPS, mais nous avons les trames AIS du bateau sur lequel nous sommes. Nous allons décoder ces trames et les transformer en trames NMEA qui seraient émises par un récepteur GPS. Il est plus simple de passer par le fichier CSV que nous sauvegardons.

Les trames NMEA183 d'un récepteur GNSS sont au même format que les trames AIS. Les talker ID et les phrases sont différents.

  $<talker ID><type>,<données séparées par des virgules>*<checksum>

Ils sont en lien avec les constellations de satellites utilisées. Vous trouverez plus d'informations sur le résumé trames GNSS

Les principaux talker ID sont :

  • GP — Données provenant du système GPS (NAVSTAR).
  • GN — Données combinant plusieurs systèmes GNSS (GPS, GLONASS, Galileo, BeiDou…).
  • GL — Données provenant du système GLONASS (Russie).
  • GA — Données provenant du système Galileo (Union européenne).
  • GB — Données provenant du système BeiDou (Chine).

Nous allons utiliser le talker ID GP pour la suite. Ce talker a plusieurs phrases possibles (sentence ID) :

  • GGA — Position GPS, qualité du fix, altitude.
  • GLL — Latitude / longitude (position) + heure.
  • GSA — Satellites utilisés pour le fix + DOP.
  • GSV — Satellites visibles + élévation/azimut/SNR.
  • RMC — Route minimale recommandée (position, vitesse, cap).
  • VTG — Cap vrai/magnétique + vitesse fond.
  • ZDA — Date et heure, information sur le fuseau.
  • GST — Estimation d’erreur de position (statistiques).
  • TXT — Messages textuels d’état ou information.

Nous allons utiliser GGA qui est le plus utilisé, et même indispensable, car plus d'informations sont transmises. Notamment la qualité de réception avec le nombre de satellites. Le type RMC est également très important, car il contient l'heure exacte et la date (que n'a pas GGA), mais pour la démonstration nous n'allons pas l'utiliser. Il suffit d'utiliser la fonction de création de la bibliothèque pynmea2 utilisée.

Lab1.3

Il faut récupérer la Latitude et la longitude du transrade puis forger une trame GPGGA puis l'envoyer sur un port en UDP.

Deux possibilités :

  • Utiliser le flux capté
  • Utiliser la sauvegarde du flux

Nous déroulons la méthode avec le flux AIS enregistré en utilisant un filtre sur le MMSI afin de créer un nouveau fichier ** gps.csv ** dont nous lisons juste la dernière ligne. Il s'agit seulement d'une démonstration, d'autres méthodes sont évidemment possibles. Traiter les données captées directement est par exemple plus efficace.

Récupération de la position du bateau

Repérer le MMSI du bateau et afficher sa position. Vous pouvez également enlever l'affichage des trames AIS du bateau afin de ne pas avoir de superposition dans OpenCPN.

Position du bateau

Le signal radio sur le bateau qui nous transporte est sûrement le plus fort donc un filtre sur la puissance peut être réalisé. Le bateau est sûrement celui qui nous envoie le plus de messages, nous pouvons également faire un filtre sur le nombre de messages reçus. Nous pouvons également nous servir du type de bateau ainsi que de son pavillon. Avoir repéré le nom du bateau en montant ou bien demander à l'équipage sont de bonnes options également :-)

À présent nous pouvons écrire l'ensemble des positions du bateau dans un CSV afin de le lire pour envoyer les positions au logiciel OpenCPN.

Création du fichier de positions
while true; do cat data.csv |grep MON_MMSI > gps.csv    

Construction des phrases NMEA

Pour construire les phrases GPS (type GPGGA), la librairie Python pynmea2 a une fonction très utile. Utilisez la pour construire vos phrases NMEA à partir du fichier CSV.

Construction des phrases GPGA
def buildGP(data):
    # Récupération des champs du fichier CSV
    ts, lat, lon, alt = data.split(",")

    # Peut on considérer que l'altitude est constante :-)
    alt="0"

    # Conversion latitude / longitude en format NMEA
    lat = float(lat)
    lon = float(lon)

    lat_deg = int(abs(lat))
    lat_min = (abs(lat) - lat_deg) * 60
    lat_hemisphere = "N" if lat >= 0 else "S"

    lon_deg = int(abs(lon))
    lon_min = (abs(lon) - lon_deg) * 60
    lon_hemisphere = "E" if lon >= 0 else "W"

    # Création d'une phrase GGA
    msg = pynmea2.GGA(
        'GP',  # talker ID (GPS)
        'GGA',
        (
            ts,                                         # heure (HHMMSS.sss) 
            f"{lat_deg:02d}{lat_min:06.3f}",            # latitude NMEA
            lat_hemisphere,
            f"{lon_deg:03d}{lon_min:06.3f}",            # longitude NMEA
            lon_hemisphere,
            "1",              # fix quality (1 = GPS)
            "8",              # nombre de satellites
            "1.0",            # HDOP
            alt,     # altitude
            "M",            # unité mètre
            "0",              # geoid separation
            "M",
            "",
            ""
        )
    )

    return sentence

Envoi des phrases en UDP

Nous allons à présent envoyer les phrases construites en UDP afin de faire une connexion de données dans OpenCPN comme pour aiscatcher.

Fonction d'envoi en UDP pour OpenCPN
def envoi(UDP_IP, UDP_PORT, fichier, delai):
    sock = socket.socket(socket.AF_INET, # Internet
                         socket.SOCK_DGRAM) # UDP
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
    print("Ctrl-C pour sortir ...")
    while True:
        try:
            last_line = lectureDerniereLigne(fichier)
            mess=buildGP(last_line)
            print("on va envoyer")
            print(mess)
            if mess!=None:
                if len(mess) < 1:
                    sock.close()
                    return True
                mess = mess.strip()
                mess = mess + u"\r\n"
                sock.sendto(mess.encode("utf-8"),(UDP_IP, UDP_PORT))
            time.sleep(delai) 
        except KeyboardInterrupt:
            sock.close()
            return True
        except Exception as msg:
            sock.close()
            return False
Pour récupérer toujours la dernière ligne
def lectureDerniereLigne(filename):
    last_line = ""
    try:
        # lire seulement la dernière ligne existante
        with open(filename, "rb") as f:
            f.seek(-2, os.SEEK_END)

            # remonter jusqu'au début de la dernière ligne
            while f.read(1) != b"\n":
                f.seek(-2, os.SEEK_CUR)

            last_line = f.readline().decode().strip()

    except OSError:
        # fichier trop petit (ex: 1 seule ligne)
        with open(filename, "r") as f:
            last_line = f.read().strip()
    print("Dernière ligne :", last_line)
    return last_line    

Créer à présent une connexion à OpenCPN afin de voir le bateau sur la carte. Si vous rencontrez des difficultés, n'hésitez pas à utiliser le script Python fourni.

Pour vérifier que le logiciel reçoit bien vos trames NMEA183, vous pouvez utiliser l'outil ** Analyse de données ** du menu Outils. Vous pourrez observer les messages reçus. La figure ci-dessous montre l'outil avec des trames de type AIVDM et GPGGA reçues par OpenCPN.

Visualisation des trames reçues par OpenCPN

**Partie au port de Brest et retour à Lanvéoc **

Nous allons à présent visualiser le fichier CSV créé sous QGis en le transformant en couche géographique. Ce logiciel est fait pour l'acquisition, la visualisation et l'analyse de données géographiques. (le Gimp des Systèmes d'Information Géographique). QGis a la possibilité d'ouvrir nativement des fichiers CSV en tant que couche géographique à la condition de lui préciser avec quels champs la géométrie des entités doit être créée.

Dans le logiciel QGis ajoutez une couche de texte délimité. Choisissez le fichier CSV créé (data.csv). Vous pouvez lancer l'enregistrement de ce fichier si vous l'avez arrêté avec la commande :

while true; do jq  -c 'select(.type|IN(1,2,3,18,19))' data.json \
        |jq -r '[.mmsi,.lat,.lon,.signalpower]|@csv'> data.csv;sleep 3; done

Vous avez également la possibilité d'utiliser ce fichier

Si vous n'avez pas mis d'entêtes sur la première ligne de votre fichier, décochez "entêtes en première ligne". Pour la définition de la géométrie, vous devez choisir la longitude pour le champ X et la latitude pour le champ Y. Le Système de Coordonnées de Référence (SCR) est "WGS84 - EPSG:4326". Il s'agit du système de coordonnées utilisé dans le monde maritime. Ce système a l'avantage de préserver les alignements, ce qui est essentiel pour la navigation pour les amers. Par contre les surfaces ne sont pas préservées et la carte peut sembler déformée. Vous pouvez sélectionner un autre SCR pour le projet (pas pour l'ajout de la couche), le SCR EPSG:3857 est plus proche de ce que l'on a l'habitude de voir. Le SCR du projet peut se changer en cliquant tout en bas à droite de la fenêtre de QGis ou bien par le menu "Projet/Propriétés".

La figure ci-dessous montre l'ajout de la couche géographique à partir du fichier CSV.

Affichage des traces AIS dans QGis

Un clic droit sur la couche dans le cartouche en bas à gauche, vous permet de zoomer sur la couche. Cela permet d'avoir l'ensemble des entités dans la fenêtre de visualisation. C'est une méthode rapide pour identifier des points aberrants !

Ajoutez une couche pour le fond cartographique de type XYZ Tiles/OSMlocal. Vous pouvez trouver cette couche dans la partie explorateur à gauche de l'interface ou bien dans le menu "Couche/Ajouter une couche XYZ". Cette couche doit être en dessous de toutes les autres couches. Vérifiez que les points sont bien placés. Si vos points sont au sud-est de l'Afrique, c'est que la longitude et la latitude sont inversées.

La figure ci-dessous montre l'ajout d'un fond cartographique.

Affichage des traces AIS dans QGis

Le fichier CSV est mis à jour régulièrement, mais QGis ne le charge qu'une seule fois. L'extension "Reloader" permet de recharger automatiquement une couche. Dans le menu "Extensions" choisissez "Start watching layer(s) for changes". Vous pouvez arrêter le chargement automatique ou recharger la couche également.

La symbologie permet de mieux visualiser les données reçues. Dans les propriétés de la couche, dans la symbologie vous pouvez faire une symbologie catégorisée et choisir le champ contenant le MMSI du bateau. Cela permet d'avoir un affichage comme celui-ci :

Affichage des traces AIS dans QGis

Le MID (Maritime Identification Digits) permet de connaître la nationalité du navire. Ce sont les 3 premiers chiffres du MMSI. Le fichier CSV MID contient la correspondance entre MID et pays. Cette correspondance est publique et peut être retrouvée sur plusieurs sites Web.

Info

Le MMSI 299999999 est indiqué soit parce que le vrai MMSI est inconnu soit pour réaliser des tests. Aucune nationalité n'est attribuée au MID 299.

Pour ajouter le champ nationalité à la couche de points, il faut réaliser une jointure avec ce CSV. Ajoutez le fichier en tant que couche texte délimité sans géométrie. Dans les propriétés de la couche de points, réalisez une jointure comme indiqué sur la figure suivante.

jointure

Vous pouvez ensuite faire une symbologie catégorisée avec la nationalité du navire. Vous avez ainsi la possibilité de visualiser rapidement les nationalités des navires et de cocher/décocher certaines comme dans la figure suivante.

carte MID

Lab1.4

Nous recevons seulement les trames AIS des bateaux qui sont à portée de notre bateau. Dans quelles mesures pouvons-nous en inférer notre propre position ?

Calcul du barycentre du nuage de points

Vous pouvez calculer le barycentre de l'ensemble des points avec "Vecteur/Outils d'analyse/Coordonnée(s) Moyenne(s)". Pensez-vous que l'on peut en déduire la position du bateau ? Comment peut-on gagner en précision ?

Calcul de la position du récepteur

Dans l'outil de calcul de coordonnées moyennes vous pouvez pondérer avec la puissance du signal reçu. Mais nous supposons que notre receveur AIS et entouré d'émetteurs or ce n'est pas le cas à cause de la côte. Cette méthode n'est donc pas un bon indicateur de la position du récepteur. Pour pouvoir utiliser les caractéristiques du signal reçu, vous pouvez utiliser le champ "signalpower". Il faudra le convertir dans QGis afin d'en faire un entier positif (un +50 fait l'affaire). Les lignes utilisées pour faire le calcul doivent être captées pendant que le bateau est à l'arrêt ou bien prendre une fenêtre temporelle de quelques minutes. Si vous voulez le faire avec le bateau en mouvement il vous faudra également le champ rxtime dans votre fichier. Sur la figure suivante vous trouverez les paramètres le calcul des coordonnées moyennes. Sur la carte, les points jaunes représentent les points sélectionnées pour faire le calcul, les triangles rouges sont les positions de transrade et le losange rouge le point de coordonnées moyennes. Barycentre

Injection de trames AIS

Nous allons créer de fausses trames AIS et les injecter dans le flux de réception de QGis puis d'OpenCPN. Pour cela nous allons utiliser QGis pour créer une suite de points réalistes qui vont nous servir de modèle. Ensuite nous allons créer les trames AIS avec ces positions à l'aide de la librairie pynmea2. Finalement, nous les enverrons en broadcast UDP pour vérifier sur OpenCPN que tout fonctionne.

Lab1.5

Création et export de la couche de points

La première étape est de créer une couche de points dans QGis. Choisissez le type ESRI Shapefile qui est le format courant en SIG. Spécifiez que vous voulez une couche de points. La figure suivante vous montre l'interface de QGis pour la création d'une nouvelle couche. nouvelle Couche

Ensuite, créez vos points en passant la couche en mode édition (icone édition ). Cliquez sur la carte pour ajouter des points avec l'outil "Nouvelle entité" (icone nouvelle entité ).

Dessinez la forme que vous voulez ou bien une trajectoire réaliste. La forme la plus connue est celle faite par Balduzzi dans une des premières publications sur la falsification d'AIS et une des plus citées.

La figure suivante donne un exemple de points possibles.

exemple

Une fois notre couche de points créée, nous devons l'exporter en CSV afin de pouvoir utiliser les données et surtout les coordonnées. Le problème est que les coordonnées ne sont pas exportées directement par QGis. Dans un premier temps il faut extraire la latitude et la longitude et les mettre dans des champs de la table attributaire. Nous en profiterons pour remplir le MMSI et la qualité du signal radio.

Pour ajouter les coordonnées, il faut ajouter de nouveaux champs à la couche et les remplir avec les fonctions : x($geom) et y($geom), voir la figure suivante :

Il ne reste plus qu'à exporter la couche de points en tant que fichier CSV.

Couche de points

Voici une couche de points au format CSV avec des coordonnées: Fichier points

Lab1.6

Vous avez une série de points avec des coordonnées, nous pouvons à présent les envoyer en tant que trames AIS à OpenCPN.

Création et émission des fausses trames AIS

La librairie pyais permet de créer rapidement des phrases AIS.

   def creationTrameAIVDM(mmsi, lat, lon, course, speed):
    msg = {
        'type': 1,  
        'mmsi': mmsi,
        'shipname': "TERENEZ",
        'callsign': "TERENEZ",
        'destination': "Brest",
        'status': 0,  
        'speed': speed,  
        'accuracy': 1,  
        'lon': lon,  
        'lat': lat, 
        'course': course,  
        'heading': 511,  
        'second': 34,  
        'maneuver': 0,
        'raim': 0,  
        'radio': 34567  # Radio status
    }
    encoded_msg = pyais.encode_dict(msg, radio_channel="B", talker_id="AIVDM")
    return encoded_msg  

Regardez l'effet sur OpenCPN.

Code complet pour l'émission de fausses trames
import sys, os
import time
import socket
import csv
from pyais.encode import encode_dict

UDP_IP="127.0.0.1"
UDP_PORT=9999
mmsi =299999000
# Lecture du CSV
with open("faussestrames.csv") as f:
    reader = csv.DictReader(f)
    for row in reader:
        #Si on garde toujours le même MMSI les points disparaissent au fur et à mesure. Avec un MMSI différent on peut dessiner :-)
        #mmsi = int(row["MMSI"])
        mmsi +=1
        lat = float(row["lat"])
        lon = float(row["lon"])
        #sog = float(row["sog"])
        #cog = float(row["cog"])
        sog = 0
        cog = 0


        # Construction d’un message AIS type 1
        # pyais gère tous les champs automatiquement
        msg = {
            "msg_type": 1,
            "repeat_indicator": 0,
            "mmsi": mmsi,
            "nav_status": 0,
            "rot": 0,
            "sog": int(sog * 10),      # format AIS = 1/10 noeud
            "pos_acc": 1,
            "lon": lon,
            "lat": lat,
            "cog": int(cog * 10),      # format AIS = 1/10°
            "true_heading": 511,
            "timestamp": 40,
        }
        message = encode_dict(msg, radio_channel="B",talker_id="AIVDM") 
        mess = message[0]
        print(mess)

        sock = socket.socket(socket.AF_INET, # Internet
                             socket.SOCK_DGRAM) # UDP
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
        mess = mess.strip()
        mess = mess + u"\r\n"
        sock.sendto(mess.encode("utf-8"),(UDP_IP, UDP_PORT))

Lab1.7

Vous avez tous les outils pour contrôler les informations reçues par OpenCPN. L'attaque la plus simple est la saturation du logiciel (écran dans un premier temps).

Deux possibilités :

  • Créer un fichier avec de nombreux points avec QGis
  • Créer les points directement en python à partir d'un point origine

Nous allons utiliser QGis car une fonction de création de grille existe.

Saturation de trames AIS

Avec QGis vous pouvez créer une grille de points à l'aide de la fonction "Créer grille" du menu "Vecteur/Outils de recherche".

La figure suivante montre les différents options. Pour l'étendue de la couche vous pouvez la dessiner à la main gràce à l'icone se trouvant à droite de la zone de saisie "étendue". Pour le SCR, utilisez "EPSG:4326". Un point tous les 100 mètres génère déjà une bonne saturation visuelle. Avec le SCR 4326 ce sont des degrés. La valeur 0,005 correspond à 100 mètres (légèrement plus).

grille

Une fois votre couche créée il faut l'exporter en CSV après avoir ajouté les champs latitude et longitude.

Saturation de l'affichage

Voici un fichier de points au format CSV : saturation

Le code complet pour la saturation de l'écran :

    import sys, os
    import time
    import socket
    import csv
    from pyais.encode import encode_dict

    UDP_IP="127.0.0.1"
    UDP_PORT=9999
    mmsi =299900000
    # Lecture du CSV
    with open("saturation.csv") as f:
        reader = csv.DictReader(f)
        for row in reader:
            #Si on garde toujours le même MMSI les points disparaissent au fur et à mesure. Avec un MMSI différent on peut dessiner :-)
            #mmsi = int(row["MMSI"])
            mmsi +=1
            lat = float(row["lat"])
            lon = float(row["lon"])
            #sog = float(row["sog"])
            #cog = float(row["cog"])
            sog = 0
            cog = 0


            # Construction d’un message AIS type 1
            # pyais gère tous les champs automatiquement
            msg = {
                "msg_type": 1,
                "repeat_indicator": 0,
                "mmsi": mmsi,
                "nav_status": 0,
                "rot": 0,
                "sog": int(sog * 10),      # format AIS = 1/10 noeud
                "pos_acc": 1,
                "lon": lon,
                "lat": lat,
                "cog": int(cog * 10),      # format AIS = 1/10°
                "true_heading": 511,
                "timestamp": 40,
            }
            message = encode_dict(msg, radio_channel="B",talker_id="AIVDM") 
            mess = message[0]
            print(mess)

            sock = socket.socket(socket.AF_INET, # Internet
                                 socket.SOCK_DGRAM) # UDP
            sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
            mess = mess.strip()
            mess = mess + u"\r\n"
            sock.sendto(mess.encode("utf-8"),(UDP_IP, UDP_PORT))

** Projet **

Pour cette partie nous n'utilisons plus les récepteurs, mais faisons l'hypothèse que nous sommes connectés au multiplexeur NMEA du bateau. Nous recevons les trames GPS du bateau et les trames AIS reçues.

Vous avez deux binaires dans le dossier personnel : AISPlayer et GPSPlayer. Ces deux exécutables fonctionnent de la même manière : ils prennent en paramètre l'IP (127.0.0.1 dans notre cas), un port et en option la durée entre chaque émission fixée à 0,1 seconde par défaut.

Commencer par exécuter ces fichiers et vérifier que vous recevez bien des données sur OpenCPN.

Lab1.8

Visualisation du flux de données

   AISPlayer 127.0.0.1 8888 &
   GPSPlayer 127.0.0.1 9999 &
FIGURE d'OpenCPN

Lab1.9

À présent que nous avons deux flux NMEA nous pouvons le détourner pour insérer de fausses informations.

Man In The Middle

Changer les ports des connexions dans OpenCPN par 8886 et 9996. Maintenant vous allez recevoir les flux dans un script python et les re transmettre sur ces nouveaux ports pour OpenCPN. Pensez à enregistrer les données au passage, vous pourrez les analyser à postériori.

Cette partie constitue un projet libre, les différentes étapes peuvent être réalisées dans le désordre. Vous pouvez appliquer l'ensemble des techniques vues précédemment.

Défi 1

Modification des MMSI

Changer le MID est très rapide, mais plutôt perturbant. Choisissez une nationalité qui pourrait perturber l'équipage.

Changer l'ensemble des navires par des navires de guerre est également une possibilité.

Ces deux modifications sont assez visibles. Par contre, échanger des MMSI est plus difficile à analyser. Vous pouvez réaliser une table d'échanges de MMSI afin de réaliser toujours les mêmes échanges et ainsi faire des traces cohérentes sur OpenCPN.

Défi 2

Modification des informations de certains navires

Plusieurs possibilités : - effacer certains bateaux selons des critères (type, MID, MMSI) - modifier certaines propriétés. - modifier les coordonnées des bateaux les plus proches afin de les rapprocher du bateau et faire ainsi déclencher des alertes.

Défi 3

Modification des trames GPS

Vous avez un flux GPS, vous pouvez le décaler lentement par exemple. Utilisez le code du lab1.3

Défi 4

Saturation de trames AIS proches du bateau

Ajoutez de nombreux bateaux autour de la position GPS du bateau.

Défi 5

Analyse et détection des trames GPS reçues

Comment détecter les fausses informations ?

Plusieurs paramètres peuvent être considérer indépendamment ou bien de manière croisée : - la vitesse - le Cap - le MMSI

Vous pouvez utiliser des scripts python et QGis pour interpréter et analyser les trames reçues.