Salut Habr. Tous ceux qui ont déjà rencontré ou accompagné des parents ou des amis dans un avion ont probablement utilisé le service gratuit Flightradar24. C'est un moyen très pratique de suivre la position de l'avion en temps réel.

La
première partie décrit le principe de fonctionnement d'un tel service en ligne. Nous allons maintenant aller plus loin et découvrir quelles données sont transmises et reçues de l'avion à la station de réception, et les décoder indépendamment en utilisant Python.
L'histoire
De toute évidence, les données sur l'avion ne sont pas transmises afin que les utilisateurs puissent les voir sur leurs smartphones. Le système est appelé ADS-B (surveillance dépendante automatique - diffusion) et est utilisé pour transmettre automatiquement les informations de l'avion à un centre de contrôle - son identifiant, ses coordonnées, sa direction, sa vitesse, son altitude et d'autres données sont transmis. Auparavant, avant l'avènement de tels systèmes, le répartiteur ne pouvait voir qu'un point sur le radar. Ce n'était pas suffisant quand il y avait trop d'avions.
Techniquement, l'ADS-B consiste en un émetteur sur un avion qui envoie périodiquement des paquets d'informations à une fréquence assez élevée de 1090 MHz (il existe d'autres modes, mais ils ne nous intéressent pas tellement, car les coordonnées ne sont transmises qu'ici). Bien sûr, en plus de l'émetteur, il y a aussi un récepteur quelque part à l'aéroport, mais pour nous, comme pour les utilisateurs, notre propre récepteur est intéressant.
Soit dit en passant, à titre de comparaison, le premier système de ce type, Airnav Radarbox, conçu pour les utilisateurs ordinaires, est apparu en 2007 et coûte environ 900 $, environ 250 $ par an valant un abonnement aux services réseau.

Vous pouvez lire les commentaires de ces premiers propriétaires russes sur le forum
radioscanner . Maintenant que les récepteurs RTL-SDR sont devenus largement disponibles, un appareil similaire peut être assemblé pour 30 $, plus à ce sujet dans la
première partie . Nous allons procéder au protocole lui-même - voyons comment cela fonctionne.
Réception de signaux
Pour commencer, le signal doit être enregistré. Le signal entier a une durée de seulement 120 microsecondes, par conséquent, afin de démonter confortablement ses composants, un récepteur SDR avec une fréquence d'échantillonnage d'au moins 5 MHz est souhaitable.

Après l'enregistrement, nous obtenons un fichier WAV avec une fréquence d'échantillonnage de 5 000 000 d'échantillons / sec, 30 secondes d'un tel enregistrement "pesant" environ 500 Mo. L'écouter avec un lecteur multimédia est bien sûr inutile - le fichier ne contient pas de son, mais un signal radio directement numérisé - c'est ainsi que fonctionne la radio définie par logiciel.
Nous allons ouvrir et traiter le fichier en utilisant Python. Ceux qui souhaitent expérimenter par eux-mêmes peuvent télécharger l'échantillon d'enregistrement à
partir du lien .
Téléchargez le fichier et voyez ce qu'il contient.
from scipy.io import wavfile import matplotlib.pyplot as plt import numpy as np fs, data = wavfile.read("adsb_20190311_191728Z_1090000kHz_RF.wav") data = data.astype(float) I, Q = data[:, 0], data[:, 1] A = np.sqrt(I*I + Q*Q) plt.plot(A) plt.show()
Résultat: on voit des «impulsions» évidentes sur fond de bruit.

Chaque «impulsion» est un signal dont la structure est clairement visible si vous augmentez la résolution sur le graphique.

Comme vous pouvez le voir, l'image est conforme à ce qui est décrit dans la description ci-dessus. Vous pouvez commencer à traiter les données.
Décodage
Vous devez d'abord obtenir un bitstream. Le signal lui-même est codé en utilisant le codage manchester:

De la différence de niveaux dans les grignotages, il est facile d'obtenir de vrais «0» et «1».
bits_str = "" for p in range(8): pos = start_data + bit_len*p p1, p2 = A[pos: pos + bit_len/2], A[pos + bit_len/2: pos + bit_len] avg1, avg2 = np.average(p1), np.average(p2) if avg1 < avg2: bits_str += "0" elif avg1 > avg2: bits_str += "1"
La structure du signal lui-même est la suivante:

Examinons les champs plus en détail.
DF (Downlink Format, 5 bits) - définit le type de message. Il en existe plusieurs types:

(
source du tableau )
Nous sommes uniquement intéressés par le type DF17, car il contient les coordonnées de l'avion.
L'OACI (24 bits) est un code d'avion international unique. Vous pouvez vérifier l'avion par son code
sur le site Web (malheureusement, l'auteur a cessé de mettre à jour la base de données, mais elle est toujours pertinente). Par exemple, pour le code 3c5ee2, nous avons les informations suivantes:

Edit: dans le
commentaire de l'article, la description du code OACI est donnée plus en détail, je vous recommande de la lire aux personnes intéressées.
DONNÉES (56 ou 112 bits) - en fait les données que nous allons décoder. Les 5 premiers bits de données sont le champ
Code de type contenant le sous-type des données stockées (à ne pas confondre avec DF). Il existe plusieurs de ces types:

(
source du tableau )
Regardons quelques exemples de packages.
Identification de l'aéronefExemple binaire:
00100 011 000101 010111 000111 110111 110001 111000
Champs de données:
+------+------+------+------+------+------+------+------+------+------+ | TC,5 | EC,3 | C1,6 | C2,6 | C3,6 | C4,6 | C5,6 | C6,6 | C7,6 | C8,6 | +------+------+------+------+------+------+------+------+------+------+
TC = 00100b = 4, chaque caractère C1-C8 contient des codes correspondant aux indices de la chaîne:
#ABCDEFGHIJKLMNOPQRSTUVWXYZ ##### _ ################## 0123456789 ######
Après avoir décodé la ligne, il est facile d'obtenir le code d'avion: EWG7184
symbols = "#ABCDEFGHIJKLMNOPQRSTUVWXYZ#####_###############0123456789######" code_str = "" for p in range(8): c = int(bits_str[8 + 6*p:8 + 6*(p + 1)], 2) code_str += symbols[c] print("Aircraft Identification:", code_str.replace('#', ''))
Position aéroportéeSi le nom est simple, les coordonnées sont plus compliquées. Ils sont transmis sous forme de trames 2x, paires et impaires. Code de champ TC = 01011b = 11.

Exemple de paquets pairs et impairs:
01011 000 000101110110 00 10111000111001000 10000110101111001 01011 000 000110010000 01 10010011110000110 10000011110001000
Le calcul des coordonnées s'effectue selon une formule assez astucieuse:

(
source )
Je ne suis pas un spécialiste des SIG, donc je ne sais pas d'où il vient. Qui est au courant, écrivez dans les commentaires.
L'altitude est considérée comme plus facile - en fonction d'un bit particulier, elle peut apparaître sous la forme d'un multiple de 25 ou 100 pieds.
Vitesse aéroportéePaquet avec TC = 19. Ce qui est intéressant ici, c'est que la vitesse peut être soit précise, par rapport au sol (Ground Speed), soit à l'air, telle que mesurée par un capteur d'avion (Airspeed). De nombreux champs différents sont également transmis:

(
source )
Conclusion
Comme vous pouvez le voir, la technologie ADS-B est devenue une symbiose intéressante quand une norme est utile non seulement pour les professionnels, mais aussi pour les utilisateurs ordinaires. Mais bien sûr, le rôle clé à cet égard a été joué par la réduction du coût de la technologie des récepteurs SDR numériques, qui permet à l'appareil de recevoir des signaux avec une fréquence supérieure à gigahertz sur l'appareil littéralement «pour un sou».
Dans la norme elle-même, bien sûr, beaucoup plus que tout. Les personnes intéressées peuvent voir le PDF sur la page de l'
OACI ou visiter le
site déjà mentionné ci-dessus.
Il est peu probable que bon nombre des éléments ci-dessus soient utiles, mais au moins l'idée générale de la façon dont cela fonctionne, j'espère, demeure.
Soit dit en passant, un décodeur prêt à l'emploi en Python existe déjà, il peut être étudié
ici . Et les propriétaires de récepteurs SDR peuvent assembler et exécuter le décodeur ADS-B fini à
partir de la page , plus à ce sujet dans la
première partie .
Le code source de l'analyseur décrit dans l'article est donné sous la coupe. Il s'agit d'un exemple de test qui ne prétend pas être de la production, mais quelque chose fonctionne, et vous pouvez analyser le fichier enregistré ci-dessus.
Code source (Python) from __future__ import print_function from scipy.io import wavfile from scipy import signal import matplotlib.pyplot as plt import numpy as np import math import sys def parse_message(data, start, bit_len): max_len = bit_len*128 A = data[start:start + max_len] A = signal.resample(A, 10*max_len) bits = np.zeros(10*max_len) bit_len *= 10 start_data = bit_len*8
J'espère que quelqu'un était intéressé, merci pour votre attention.