Flightradar24 β€” ΠΊΠ°ΠΊ это Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚? Π§Π°ΡΡ‚ΡŒ 2, ADS-B ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»

ΠŸΡ€ΠΈΠ²Π΅Ρ‚ Π₯Π°Π±Ρ€. НавСрноС ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ, ΠΊΡ‚ΠΎ Ρ…ΠΎΡ‚ΡŒ Ρ€Π°Π· встрСчал ΠΈΠ»ΠΈ ΠΏΡ€ΠΎΠ²ΠΎΠΆΠ°Π» родствСнников ΠΈΠ»ΠΈ Π΄Ρ€ΡƒΠ·Π΅ΠΉ Π½Π° самолСт, пользовался бСсплатным сСрвисом Flightradar24. Π­Ρ‚ΠΎ вСсьма ΡƒΠ΄ΠΎΠ±Π½Ρ‹ΠΉ способ отслСТивания полоТСния самолСта Π² Ρ€Π΅Π°Π»ΡŒΠ½ΠΎΠΌ Π²Ρ€Π΅ΠΌΠ΅Π½ΠΈ.

Flightradar24 — ΠΊΠ°ΠΊ это Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚? Π§Π°ΡΡ‚ΡŒ 2, ADS-B ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»

Π’ ΠΏΠ΅Ρ€Π²ΠΎΠΉ части Π±Ρ‹Π» описан ΠΏΡ€ΠΈΠ½Ρ†ΠΈΠΏ Ρ€Π°Π±ΠΎΡ‚Ρ‹ Ρ‚Π°ΠΊΠΎΠ³ΠΎ ΠΎΠ½Π»Π°ΠΉΠ½-сСрвиса. БСйчас ΠΌΡ‹ ΠΏΠΎΠΉΠ΄Π΅ΠΌ дальшС, ΠΈ выясним, ΠΊΠ°ΠΊΠΈΠ΅ Π΄Π°Π½Π½Ρ‹Π΅ ΠΏΠ΅Ρ€Π΅Π΄Π°ΡŽΡ‚ΡΡ ΠΈ ΠΏΡ€ΠΈΠ½ΠΈΠΌΠ°ΡŽΡ‚ΡΡ ΠΎΡ‚ Π²ΠΎΠ·Π΄ΡƒΡˆΠ½ΠΎΠ³ΠΎ судна ΠΊ ΠΏΡ€ΠΈΠ΅ΠΌΠ½ΠΎΠΉ станции, ΠΈ Π΄Π΅ΠΊΠΎΠ΄ΠΈΡ€ΡƒΠ΅ΠΌ ΠΈΡ… ΡΠ°ΠΌΠΎΡΡ‚ΠΎΡΡ‚Π΅Π»ΡŒΠ½ΠΎ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ Python.

Π˜ΡΡ‚ΠΎΡ€ΠΈΡ

ΠžΡ‡Π΅Π²ΠΈΠ΄Π½ΠΎ, Ρ‡Ρ‚ΠΎ Π΄Π°Π½Π½Ρ‹Π΅ ΠΎ самолСтах ΠΏΠ΅Ρ€Π΅Π΄Π°ΡŽΡ‚ΡΡ Π½Π΅ для Ρ‚ΠΎΠ³ΠΎ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΠΈ Π²ΠΈΠ΄Π΅Π»ΠΈ ΠΈΡ… Π½Π° своих смартфонах. БистСма называСтся ADS–B (Automatic dependent surveillanceβ€”broadcast), ΠΈ слуТит для автоматичСской ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‡ΠΈ ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΠΈ ΠΎ Π²ΠΎΠ·Π΄ΡƒΡˆΠ½ΠΎΠΌ суднС Π² диспСтчСрский Ρ†Π΅Π½Ρ‚Ρ€ β€” ΠΏΠ΅Ρ€Π΅Π΄Π°ΡŽΡ‚ΡΡ Π΅Π³ΠΎ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€, ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚Ρ‹, Π½Π°ΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΠ΅, ΡΠΊΠΎΡ€ΠΎΡΡ‚ΡŒ, высота ΠΈ ΠΏΡ€ΠΎΡ‡ΠΈΠ΅ Π΄Π°Π½Π½Ρ‹Π΅. Π Π°Π½Π΅Π΅, Π΄ΠΎ появлСния Ρ‚Π°ΠΊΠΈΡ… систСм, диспСтчСр ΠΌΠΎΠ³ Π²ΠΈΠ΄Π΅Ρ‚ΡŒ лишь Ρ‚ΠΎΡ‡ΠΊΡƒ Π½Π° Ρ€Π°Π΄Π°Ρ€Π΅. Π­Ρ‚ΠΎΠ³ΠΎ стало нСдостаточно, ΠΊΠΎΠ³Π΄Π° самолСтов стало слишком ΠΌΠ½ΠΎΠ³ΠΎ.

ВСхничСски, ADS-B состоит ΠΈΠ· ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠ° Π½Π° Π²ΠΎΠ·Π΄ΡƒΡˆΠ½ΠΎΠΌ суднС, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ пСриодичСски посылаСт ΠΏΠ°ΠΊΠ΅Ρ‚Ρ‹ с ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΠ΅ΠΉ Π½Π° достаточно высокой частотС 1090 ΠœΠ“Ρ† (Π΅ΡΡ‚ΡŒ ΠΈ Π΄Ρ€ΡƒΠ³ΠΈΠ΅ Ρ€Π΅ΠΆΠΈΠΌΡ‹, Π½ΠΎ Π½Π°ΠΌ ΠΎΠ½ΠΈ Π½Π΅ Ρ‚Π°ΠΊ интСрСсСны, Ρ‚.ΠΊ. ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚Ρ‹ ΠΏΠ΅Ρ€Π΅Π΄Π°ΡŽΡ‚ΡΡ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ здСсь). РазумССтся, ΠΊΡ€ΠΎΠΌΠ΅ ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠ°, Π΅ΡΡ‚ΡŒ ΠΈ ΠΏΡ€ΠΈΠ΅ΠΌΠ½ΠΈΠΊ Π³Π΄Π΅-Ρ‚ΠΎ Π² аэропорту, Π½ΠΎ для нас, ΠΊΠ°ΠΊ для ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Π΅ΠΉ, интСрСсСн ΠΏΡ€ΠΈΠ΅ΠΌΠ½ΠΈΠΊ наш собствСнный.

ΠšΡΡ‚Π°Ρ‚ΠΈ, для сравнСния, пСрвая такая систСма, Airnav Radarbox, расчитанная Π½Π° ΠΎΠ±Ρ‹Ρ‡Π½Ρ‹Ρ… ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Π΅ΠΉ, появилась Π² 2007 Π³ΠΎΠ΄Ρƒ, ΠΈ стоила ΠΎΠΊΠΎΠ»ΠΎ 900$, Π΅Ρ‰Π΅ ΠΎΠΊΠΎΠ»ΠΎ 250$ Π² Π³ΠΎΠ΄ стоила подписка Π½Π° сСтСвыС сСрвисы.

Flightradar24 — ΠΊΠ°ΠΊ это Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚? Π§Π°ΡΡ‚ΡŒ 2, ADS-B ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»

ΠžΡ‚Π·Ρ‹Π²Ρ‹ Ρ‚Π΅Ρ… ΠΏΠ΅Ρ€Π²Ρ‹Ρ… российских Π²Π»Π°Π΄Π΅Π»ΡŒΡ†Π΅Π² ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠΎΡ‡ΠΈΡ‚Π°Ρ‚ΡŒ Π½Π° Ρ„ΠΎΡ€ΡƒΠΌΠ΅ radioscanner. БСйчас, ΠΊΠΎΠ³Π΄Π° массово стали доступны RTL-SDR ΠΏΡ€ΠΈΠ΅ΠΌΠ½ΠΈΠΊΠΈ, Π°Π½Π°Π»ΠΎΠ³ΠΈΡ‡Π½Ρ‹ΠΉ дСвайс ΠΌΠΎΠΆΠ½ΠΎ ΡΠΎΠ±Ρ€Π°Ρ‚ΡŒ Π·Π° 30$, ΠΏΠΎΠ΄Ρ€ΠΎΠ±Π½Π΅Π΅ ΠΎΠ± этом Π±Ρ‹Π»ΠΎ Π² ΠΏΠ΅Ρ€Π²ΠΎΠΉ части. ΠœΡ‹ ΠΆΠ΅ ΠΏΠ΅Ρ€Π΅ΠΉΠ΄Π΅ΠΌ собствСнно, ΠΊ ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»Ρƒ β€” посмотрим ΠΊΠ°ΠΊ это Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚.

ΠŸΡ€ΠΈΠ΅ΠΌ сигналов

Для Π½Π°Ρ‡Π°Π»Π°, сигнал Π½ΡƒΠΆΠ½ΠΎ Π·Π°ΠΏΠΈΡΠ°Ρ‚ΡŒ. Π’Π΅ΡΡŒ сигнал ΠΈΠΌΠ΅Π΅Ρ‚ Π΄Π»ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΡŒ всСго лишь 120 микросСкунд, поэтому Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΊΠΎΠΌΡ„ΠΎΡ€Ρ‚Π½ΠΎ Ρ€Π°Π·ΠΎΠ±Ρ€Π°Ρ‚ΡŒ Π΅Π³ΠΎ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Ρ‹, ΠΆΠ΅Π»Π°Ρ‚Π΅Π»Π΅Π½ SDR-ΠΏΡ€ΠΈΠ΅ΠΌΠ½ΠΈΠΊ с частотой дискрСтизации Π½Π΅ ΠΌΠ΅Π½Π΅Π΅ 5ΠœΠ“Ρ†.

Flightradar24 — ΠΊΠ°ΠΊ это Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚? Π§Π°ΡΡ‚ΡŒ 2, ADS-B ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»

ПослС записи ΠΌΡ‹ ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ WAV-Ρ„Π°ΠΉΠ» с частотой дискрСтизации 5000000 сСмплов/сСк, 30 сСкунд Ρ‚Π°ΠΊΠΎΠΉ записи «вСсят» ΠΎΠΊΠΎΠ»ΠΎ 500Мб. Π‘Π»ΡƒΡˆΠ°Ρ‚ΡŒ Π΅Ρ‘ ΠΌΠ΅Π΄ΠΈΠ°ΠΏΠ»Π΅Π΅Ρ€ΠΎΠΌ разумССтся, бСсполСзно β€” Ρ„Π°ΠΉΠ» содСрТит Π½Π΅ Π·Π²ΡƒΠΊ, Π° нСпосрСдствСнно ΠΎΡ†ΠΈΡ„Ρ€ΠΎΠ²Π°Π½Π½Ρ‹ΠΉ радиосигнал β€” ΠΈΠΌΠ΅Π½Π½ΠΎ Ρ‚Π°ΠΊ Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ Software Defined Radio.

ΠžΡ‚ΠΊΡ€Ρ‹Π²Π°Ρ‚ΡŒ ΠΈ ΠΎΠ±Ρ€Π°Π±Π°Ρ‚Ρ‹Π²Π°Ρ‚ΡŒ Ρ„Π°ΠΉΠ» ΠΌΡ‹ Π±ΡƒΠ΄Π΅ΠΌ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ Python. Π–Π΅Π»Π°ΡŽΡ‰ΠΈΠ΅ ΠΏΠΎΡΠΊΡΠΏΠ΅Ρ€ΠΈΠΌΠ΅Π½Ρ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ ΡΠ°ΠΌΠΎΡΡ‚ΠΎΡΡ‚Π΅Π»ΡŒΠ½ΠΎ, ΠΌΠΎΠ³ΡƒΡ‚ ΡΠΊΠ°Ρ‡Π°Ρ‚ΡŒ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ записи ΠΏΠΎ ссылкС.

Π—Π°Π³Ρ€ΡƒΠ·ΠΈΠΌ Ρ„Π°ΠΉΠ», ΠΈ посмотрим Ρ‡Ρ‚ΠΎ Π²Π½ΡƒΡ‚Ρ€ΠΈ.

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()

Π Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚: ΠΌΡ‹ Π²ΠΈΠ΄ΠΈΠΌ явныС Β«ΠΈΠΌΠΏΡƒΠ»ΡŒΡΡ‹Β» Π½Π° Ρ„ΠΎΠ½Π΅ ΡˆΡƒΠΌΠ°.

Flightradar24 — ΠΊΠ°ΠΊ это Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚? Π§Π°ΡΡ‚ΡŒ 2, ADS-B ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»

ΠšΠ°ΠΆΠ΄Ρ‹ΠΉ Β«ΠΈΠΌΠΏΡƒΠ»ΡŒΡΒ» β€” это ΠΈ Π΅ΡΡ‚ΡŒ сигнал, структуру ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ³ΠΎ Ρ…ΠΎΡ€ΠΎΡˆΠΎ Π²ΠΈΠ΄Π½ΠΎ, Ссли ΡƒΠ²Π΅Π»ΠΈΡ‡ΠΈΡ‚ΡŒ Ρ€Π°Π·Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅ Π½Π° Π³Ρ€Π°Ρ„ΠΈΠΊΠ΅.

Flightradar24 — ΠΊΠ°ΠΊ это Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚? Π§Π°ΡΡ‚ΡŒ 2, ADS-B ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»

Как ΠΌΠΎΠΆΠ½ΠΎ Π²ΠΈΠ΄Π΅Ρ‚ΡŒ, ΠΊΠ°Ρ€Ρ‚ΠΈΠ½ΠΊΠ° Π²ΠΏΠΎΠ»Π½Π΅ соотвСтствуСт Ρ‚ΠΎΠΌΡƒ, Ρ‡Ρ‚ΠΎ ΠΏΡ€ΠΈΠ²Π΅Π΄Π΅Π½ΠΎ Π² описании Π²Ρ‹ΡˆΠ΅. МоТно ΠΏΡ€ΠΈΡΡ‚ΡƒΠΏΠ°Ρ‚ΡŒ ΠΊ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ΅ Π΄Π°Π½Π½Ρ‹Ρ….

Π”Π΅ΠΊΠΎΠ΄ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅

Для Π½Π°Ρ‡Π°Π»Π°, Π½ΡƒΠΆΠ½ΠΎ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ Π±ΠΈΡ‚ΠΎΠ²Ρ‹ΠΉ ΠΏΠΎΡ‚ΠΎΠΊ. Π‘Π°ΠΌ сигнал Π·Π°ΠΊΠΎΠ΄ΠΈΡ€ΠΎΠ²Π°Π½ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ manchester encoding:

Flightradar24 — ΠΊΠ°ΠΊ это Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚? Π§Π°ΡΡ‚ΡŒ 2, ADS-B ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»

Из Ρ€Π°Π·Π½ΠΈΡ†Ρ‹ ΡƒΡ€ΠΎΠ²Π½Π΅ΠΉ Π² ΠΏΠΎΠ»ΡƒΠ±Π°ΠΉΡ‚Π°Ρ… Π»Π΅Π³ΠΊΠΎ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ Ρ€Π΅Π°Π»ΡŒΠ½Ρ‹Π΅ Β«0Β» ΠΈ Β«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"

Π‘Ρ‚Ρ€ΡƒΠΊΡ‚ΡƒΡ€Π° самого сигнала ΠΈΠΌΠ΅Π΅Ρ‚ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΉ Π²ΠΈΠ΄:

Flightradar24 — ΠΊΠ°ΠΊ это Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚? Π§Π°ΡΡ‚ΡŒ 2, ADS-B ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»

Рассмотрим поля Π±ΠΎΠ»Π΅Π΅ ΠΏΠΎΠ΄Ρ€ΠΎΠ±Π½ΠΎ.

DF (Downlink Format, 5 Π±ΠΈΡ‚) β€” опрСдСляСт Ρ‚ΠΈΠΏ сообщСния. Π˜Ρ… нСсколько Ρ‚ΠΈΠΏΠΎΠ²:

Flightradar24 — ΠΊΠ°ΠΊ это Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚? Π§Π°ΡΡ‚ΡŒ 2, ADS-B ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»
(источник Ρ‚Π°Π±Π»ΠΈΡ†Ρ‹)

Нас интСрСсуСт Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Ρ‚ΠΈΠΏ DF17, Ρ‚.ΠΊ. ΠΈΠΌΠ΅Π½Π½ΠΎ ΠΎΠ½ содСрТит ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚Ρ‹ Π²ΠΎΠ·Π΄ΡƒΡˆΠ½ΠΎΠ³ΠΎ судна.

ICAO (24 Π±ΠΈΡ‚Π°) β€” ΠΌΠ΅ΠΆΠ΄ΡƒΠ½Π°Ρ€ΠΎΠ΄Π½Ρ‹ΠΉ ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½Ρ‹ΠΉ ΠΊΠΎΠ΄ Π²ΠΎΠ·Π΄ΡƒΡˆΠ½ΠΎΠ³ΠΎ судна. ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒ самолСт ΠΏΠΎ Π΅Π³ΠΎ ΠΊΠΎΠ΄Ρƒ ΠΌΠΎΠΆΠ½ΠΎ Π½Π° сайтС (ΠΊ соТалСнию, Π°Π²Ρ‚ΠΎΡ€ пСрСстал ΠΎΠ±Π½ΠΎΠ²Π»ΡΡ‚ΡŒ Π±Π°Π·Ρƒ, Π½ΠΎ ΠΎΠ½Π° Π΅Ρ‰Π΅ Π°ΠΊΡ‚ΡƒΠ°Π»ΡŒΠ½Π°). К ΠΏΡ€ΠΈΠΌΠ΅Ρ€Ρƒ, для ΠΊΠΎΠ΄Π° 3c5ee2 ΠΈΠΌΠ΅Π΅ΠΌ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΡƒΡŽ ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΡŽ:

Flightradar24 — ΠΊΠ°ΠΊ это Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚? Π§Π°ΡΡ‚ΡŒ 2, ADS-B ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»

ΠŸΡ€Π°Π²ΠΊΠ°: Π² ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΈ ΠΊ ΡΡ‚Π°Ρ‚ΡŒΠ΅ описаниС ΠΊΠΎΠ΄Π° ICAO ΠΏΡ€ΠΈΠ²Π΅Π΄Π΅Π½ΠΎ Π±ΠΎΠ»Π΅Π΅ ΠΏΠΎΠ΄Ρ€ΠΎΠ±Π½ΠΎ, ΠΈΠ½Ρ‚Π΅Ρ€Π΅ΡΡƒΡŽΡ‰ΠΈΠΌΡΡ Ρ€Π΅ΠΊΠΎΠΌΠ΅Π½Π΄ΡƒΡŽ ΠΎΠ·Π½Π°ΠΊΠΎΠΌΠΈΡ‚ΡŒΡΡ.

DATA (56 ΠΈΠ»ΠΈ 112 Π±ΠΈΡ‚) β€” собствСнно Π΄Π°Π½Π½Ρ‹Π΅, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΌΡ‹ ΠΈ Π±ΡƒΠ΄Π΅ΠΌ Π΄Π΅ΠΊΠΎΠ΄ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ. ΠŸΠ΅Ρ€Π²Ρ‹Π΅ 5 Π±ΠΈΡ‚ Π΄Π°Π½Π½Ρ‹Ρ… β€” ΠΏΠΎΠ»Π΅ Type Code, содСрТащСС ΠΏΠΎΠ΄Ρ‚ΠΈΠΏ хранящихся Π΄Π°Π½Π½Ρ‹Ρ… (Π½Π΅ ΠΏΡƒΡ‚Π°Ρ‚ΡŒ с DF). Π’Π°ΠΊΠΈΡ… Ρ‚ΠΈΠΏΠΎΠ² довольно ΠΌΠ½ΠΎΠ³ΠΎ:

Flightradar24 — ΠΊΠ°ΠΊ это Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚? Π§Π°ΡΡ‚ΡŒ 2, ADS-B ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»
(источник Ρ‚Π°Π±Π»ΠΈΡ†Ρ‹)

Π Π°Π·Π±Π΅Ρ€Π΅ΠΌ нСсколько ΠΏΡ€ΠΈΠΌΠ΅Ρ€ΠΎΠ² ΠΏΠ°ΠΊΠ΅Ρ‚ΠΎΠ².

Aircraft identification

ΠŸΡ€ΠΈΠΌΠ΅Ρ€ Π² Π±ΠΈΠ½Π°Ρ€Π½ΠΎΠΌ Π²ΠΈΠ΄Π΅:

00100 011 000101 010111 000111 110111 110001 111000

Поля Π΄Π°Π½Π½Ρ‹Ρ…:

+------+------+------+------+------+------+------+------+------+------+
| TC,5 | EC,3 | C1,6 | C2,6 | C3,6 | C4,6 | C5,6 | C6,6 | C7,6 | C8,6 |
+------+------+------+------+------+------+------+------+------+------+

TC = 00100b = 4, ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ символ C1-C8 содСрТит ΠΊΠΎΠ΄Ρ‹, ΡΠΎΠΎΡ‚Π²Π΅Ρ‚ΡΡ‚Π²ΡƒΡŽΡ‰ΠΈΠ΅ индСксам Π² строкС:
#ABCDEFGHIJKLMNOPQRSTUVWXYZ#####_###############0123456789######

Раскодировав строку, нСслоТно ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ ΠΊΠΎΠ΄ самолСта: 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('#', ''))

Airborne position

Если с Π½Π°Π·Π²Π°Π½ΠΈΠ΅ΠΌ всС просто, Ρ‚ΠΎ с ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚Π°ΠΌΠΈ послоТнСС. Они ΠΏΠ΅Ρ€Π΅Π΄Π°ΡŽΡ‚ΡΡ Π² Π²ΠΈΠ΄Π΅ 2Ρ…, Ρ‡Π΅Ρ‚Π½Ρ‹Ρ… ΠΈ Π½Π΅Ρ‡Π΅Ρ‚Π½Ρ‹Ρ… Ρ„Ρ€Π΅ΠΉΠΌΠΎΠ². Код поля TC = 01011b = 11.

Flightradar24 — ΠΊΠ°ΠΊ это Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚? Π§Π°ΡΡ‚ΡŒ 2, ADS-B ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»

ΠŸΡ€ΠΈΠΌΠ΅Ρ€ Ρ‡Π΅Ρ‚Π½ΠΎΠ³ΠΎ ΠΈ Π½Π΅Ρ‡Π΅Ρ‚Π½ΠΎΠ³ΠΎ ΠΏΠ°ΠΊΠ΅Ρ‚ΠΎΠ²:

01011 000 000101110110 00 10111000111001000 10000110101111001
01011 000 000110010000 01 10010011110000110 10000011110001000

Π‘Π°ΠΌΠΎ вычислСниС ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚ происходит ΠΏΠΎ достаточно Ρ…ΠΈΡ‚Ρ€ΠΎΠΉ Ρ„ΠΎΡ€ΠΌΡƒΠ»Π΅:

Flightradar24 — ΠΊΠ°ΠΊ это Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚? Π§Π°ΡΡ‚ΡŒ 2, ADS-B ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»
(источник)

Π― Π½Π΅ спСциалист ΠΏΠΎ Π“Π˜Π‘, Ρ‚Π°ΠΊ Ρ‡Ρ‚ΠΎ ΠΎΡ‚ΠΊΡƒΠ΄Π° ΠΎΠ½ΠΎ выводится, Π½Π΅ знаю. ΠšΡ‚ΠΎ Π² курсС, Π½Π°ΠΏΠΈΡˆΠΈΡ‚Π΅ Π² коммСнтариях.

Высота считаСтся ΠΏΡ€ΠΎΡ‰Π΅ β€” Π² зависимости ΠΎΡ‚ ΠΎΠΏΡ€Π΅Π΄Π΅Π»Π΅Π½Π½ΠΎΠ³ΠΎ Π±ΠΈΡ‚Π°, ΠΎΠ½Π° ΠΌΠΎΠΆΠ΅Ρ‚ ΠΏΡ€Π΅Π΄ΡΡ‚Π°Π²Π»ΡΡ‚ΡŒΡΡ Π»ΠΈΠ±ΠΎ ΠΊΡ€Π°Ρ‚Π½ΠΎΠΉ 25, Π»ΠΈΠ±ΠΎ 100 Ρ„ΡƒΡ‚Π°ΠΌ.

Airborne Velocity

ΠŸΠ°ΠΊΠ΅Ρ‚ с TC=19. Π˜Π½Ρ‚Π΅Ρ€Π΅ΡΠ½ΠΎ Ρ‚ΡƒΡ‚ Ρ‚ΠΎ, Ρ‡Ρ‚ΠΎ ΡΠΊΠΎΡ€ΠΎΡΡ‚ΡŒ ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ ΠΊΠ°ΠΊ точная, ΠΎΡ‚Π½ΠΎΡΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ Π·Π΅ΠΌΠ»ΠΈ (Ground Speed), Ρ‚Π°ΠΊ ΠΈ Π²ΠΎΠ·Π΄ΡƒΡˆΠ½Π°Ρ, измСряСмая Π΄Π°Ρ‚Ρ‡ΠΈΠΊΠΎΠΌ самолСта (Airspeed). Π•Ρ‰Π΅ пСрСдаСтся мноТСство Ρ€Π°Π·Π½Ρ‹Ρ… ΠΏΠΎΠ»Π΅ΠΉ:

Flightradar24 — ΠΊΠ°ΠΊ это Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚? Π§Π°ΡΡ‚ΡŒ 2, ADS-B ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»
(источник)

Π—Π°ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅

Как ΠΌΠΎΠΆΠ½ΠΎ Π²ΠΈΠ΄Π΅Ρ‚ΡŒ, тСхнология ADS-B стала интСрСсным симбиозом, ΠΊΠΎΠ³Π΄Π° ΠΊΠ°ΠΊΠΎΠΉ-Π»ΠΈΠ±ΠΎ стандарт пригоТдаСтся Π½Π΅ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ профСссионалам, Π½ΠΎ ΠΈ ΠΎΠ±Ρ‹Ρ‡Π½Ρ‹ΠΌ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡΠΌ. Но разумССтся, ΠΊΠ»ΡŽΡ‡Π΅Π²ΡƒΡŽ Ρ€ΠΎΠ»ΡŒ Π² этом сыграло ΡƒΠ΄Π΅ΡˆΠ΅Π²Π»Π΅Π½ΠΈΠ΅ Ρ‚Π΅Ρ…Π½ΠΎΠ»ΠΎΠ³ΠΈΠΈ Ρ†ΠΈΡ„Ρ€ΠΎΠ²Ρ‹Ρ… SDR-ΠΏΡ€ΠΈΠ΅ΠΌΠ½ΠΈΠΊΠΎΠ², ΠΏΠΎΠ·Π²ΠΎΠ»ΡΡŽΡ‰ΠΈΠΌ Π½Π° дСвайсС Π±ΡƒΠΊΠ²Π°Π»ΡŒΠ½ΠΎ Β«Π·Π° ΠΊΠΎΠΏΠ΅ΠΉΠΊΠΈΒ» ΠΏΡ€ΠΈΠ½ΠΈΠΌΠ°Ρ‚ΡŒ сигналы с частотой Π²Ρ‹ΡˆΠ΅ Π³ΠΈΠ³Π°Π³Π΅Ρ€Ρ†Π°.

Π’ самом стандартС разумССтся, Π³ΠΎΡ€Π°Π·Π΄ΠΎ большС всСго. Π–Π΅Π»Π°ΡŽΡ‰ΠΈΠ΅ ΠΌΠΎΠ³ΡƒΡ‚ ΠΏΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ PDF Π½Π° страницС ICAO ΠΈΠ»ΠΈ ΠΏΠΎΡΠ΅Ρ‚ΠΈΡ‚ΡŒ ΡƒΠΆΠ΅ упомянутый Π²Ρ‹ΡˆΠ΅ сайт.

Вряд Π»ΠΈ ΠΌΠ½ΠΎΠ³ΠΈΠΌ пригодится всС Π²Ρ‹ΡˆΠ΅Π½Π°ΠΏΠΈΡΠ°Π½Π½ΠΎΠ΅, Π½ΠΎ ΠΏΠΎ ΠΊΡ€Π°ΠΉΠ½Π΅ΠΉ ΠΌΠ΅Ρ€Π΅ общая идСя Ρ‚ΠΎΠ³ΠΎ, ΠΊΠ°ΠΊ это Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚, надСюсь, ΠΎΡΡ‚Π°Π»Π°ΡΡŒ.

ΠšΡΡ‚Π°Ρ‚ΠΈ, Π³ΠΎΡ‚ΠΎΠ²Ρ‹ΠΉ Π΄Π΅ΠΊΠΎΠ΄Π΅Ρ€ Π½Π° Python ΡƒΠΆΠ΅ сущСствуСт, Π΅Π³ΠΎ ΠΌΠΎΠΆΠ½ΠΎ ΠΈΠ·ΡƒΡ‡ΠΈΡ‚ΡŒ здСсь. А Π²Π»Π°Π΄Π΅Π»ΡŒΡ†Ρ‹ SDR-ΠΏΡ€ΠΈΠ΅ΠΌΠ½ΠΈΠΊΠΎΠ² ΠΌΠΎΠ³ΡƒΡ‚ ΡΠΎΠ±Ρ€Π°Ρ‚ΡŒ ΠΈ Π·Π°ΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ Π³ΠΎΡ‚ΠΎΠ²Ρ‹ΠΉ ADS-B Π΄Π΅ΠΊΠΎΠ΄Π΅Ρ€ со страницы, ΠΏΠΎΠ΄Ρ€ΠΎΠ±Π½Π΅Π΅ ΠΎΠ± этом Ρ€Π°ΡΡΠΊΠ°Π·Ρ‹Π²Π°Π»ΠΎΡΡŒ Π² ΠΏΠ΅Ρ€Π²ΠΎΠΉ части.

Π˜ΡΡ…ΠΎΠ΄Π½Ρ‹ΠΉ ΠΊΠΎΠ΄ парсСра, описанный Π² ΡΡ‚Π°Ρ‚ΡŒΠ΅, ΠΏΡ€ΠΈΠ²Π΅Π΄Π΅Π½ ΠΏΠΎΠ΄ ΠΊΠ°Ρ‚ΠΎΠΌ. Π­Ρ‚ΠΎ тСстовый ΠΏΡ€ΠΈΠΌΠ΅Ρ€, Π½Π΅ ΠΏΡ€Π΅Ρ‚Π΅Π½Π΄ΡƒΡŽΡ‰ΠΈΠΉ Π½Π° production, Π½ΠΎ ΠΊΠΎΠ΅-Ρ‡Ρ‚ΠΎ Π² Π½Π΅ΠΌ Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚, ΠΈ ΠΏΠ°Ρ€ΡΠΈΡ‚ΡŒ записанный Π²Ρ‹ΡˆΠ΅ Ρ„Π°ΠΉΠ», ΠΈΠΌ ΠΌΠΎΠΆΠ½ΠΎ.
Π˜ΡΡ…ΠΎΠ΄Π½Ρ‹ΠΉ ΠΊΠΎΠ΄ (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
# Parse first 8 bits
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"
df = int(bits_str[0:5], 2)
# Aircraft address (db - https://junzis.com/adb/?q=3b1c5c )
bits_str = ""
for p in range(8, 32):
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"
# print "Aircraft address:", bits_str, hex(int(bits_str, 2))
address = hex(int(bits_str, 2))
# Filter specific aircraft (optional)
# if address != "0x3c5ee2":
#    return
if df == 16 or df == 17 or df == 18 or df == 19 or df == 20 or df == 21:
# print "Pos:", start, "DF:", msg_type
# Data (56bit)
bits_str = ""
for p in range(32, 88):
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"
# bits[pos + bit_len / 2] = 50
elif avg1 > avg2:
bits_str += "1"
# http://www.lll.lu/~edward/edward/adsb/DecodingADSBposition.html
# print "Data:"
# print bits_str[:8], bits_str[8:20],  bits_str[20:22], bits_str[22:22+17], bits_str[39:39+17]
# Type Code:
tc, ec = int(bits_str[:5], 2), int(bits_str[5:8], 2)
# print("DF:", df, "TC:", tc)
# 1 - 4  Aircraft identification
# 5 - 8  Surface position
# 9 - 18  Airborne position (w/ Baro Altitude)
# 19  Airborne velocities
if tc >= 1 and tc <= 4: # and (df == 17 or df == 18):
print("Aircraft address:", address)
print("Data:")
print(bits_str[:8], bits_str[8:14],  bits_str[14:20], bits_str[20:26], bits_str[26:32], bits_str[32:38], bits_str[38:44])
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('#', ''))
print()
if tc == 11:
print("Aircraft address:", address)
print("Data: (11)")
print(bits_str[:8], bits_str[8:20],  bits_str[20:22], bits_str[22:22+17], bits_str[39:39+17])
# Bit 22 contains the F flag which indicates which CPR format is used (odd or even)
# First frame has F flag = 0 so is even and the second frame has F flag = 1 so odd
# f = bits_str[21:22]
# print("F:", int(f, 2))
# Altitude
alt1b = bits_str[8:20]
if alt1b[-5] == '1':
bits = alt1b[:-5] + alt1b[-4:]
n = int(bits, 2)
alt_ft = n*25 - 1000
print("Alt (ft)", alt_ft)
# lat_dec = int(bits_str[22:22+17], 2)
# lon_dec = int(bits_str[39:39+17], 2)
# print("Lat/Lon:", lat_dec, lon_dec)
# http://airmetar.main.jp/radio/ADS-B%20Decoding%20Guide.pdf
print()
if tc == 19:
print("Aircraft address:", address)
print("Data:")
# print(bits_str)
print(bits_str[:5], bits_str[5:8], bits_str[8:10], bits_str[10:13], bits_str[13] ,bits_str[14:24], bits_str[24], bits_str[25:35], bits_str[35:36], bits_str[36:65])
subtype = int(bits_str[5:8], 2)
# https://mode-s.org/decode/adsb/airborne-velocity.html
spd, hdg, rocd = -1, -1, -1
if subtype == 1 or subtype == 2:
print("Velocity Subtype 1: Ground speed")
v_ew_sign = int(bits_str[13], 2)
v_ew = int(bits_str[14:24], 2) - 1       # east-west velocity
v_ns_sign = int(bits_str[24], 2)
v_ns = int(bits_str[25:35], 2) - 1       # north-south velocity
v_we = -1*v_ew if v_ew_sign else v_ew
v_sn = -1*v_ns if v_ns_sign else v_ns
spd = math.sqrt(v_sn*v_sn + v_we*v_we)  # unit in kts
hdg = math.atan2(v_we, v_sn)
hdg = math.degrees(hdg)                 # convert to degrees
hdg = hdg if hdg >= 0 else hdg + 360    # no negative val
if subtype == 3:
print("Subtype Subtype 3: Airspeed")
hdg = int(bits_str[14:24], 2)/1024.0*360.0
spd = int(bits_str[25:35], 2)
vr_sign = int(bits_str[36], 2)
vr = int(bits_str[36:45], 2)
rocd = -1*vr if vr_sign else vr         # rate of climb/descend
print("Speed (kts):", spd, "Rate:", rocd, "Heading:", hdg)
print()
# print()
def calc_coordinates():
def _cprN(lat, is_odd):
nl = _cprNL(lat) - is_odd
return nl if nl > 1 else 1
def _cprNL(lat):
try:
nz = 15
a = 1 - math.cos(math.pi / (2 * nz))
b = math.cos(math.pi / 180.0 * abs(lat)) ** 2
nl = 2 * math.pi / (math.acos(1 - a/b))
return int(math.floor(nl))
except:
# happens when latitude is +/-90 degree
return 1
def floor_(x):
return int(math.floor(x))
lat1b, lon1b, alt1b = "10111000111010011", "10000110111111000", "000101111001"
lat2b, lon2b, alt2b = "10010011101011100", "10000011000011011", "000101110111"
lat1, lon1, alt1 = int(lat1b, 2), int(lon1b, 2), int(alt1b, 2)
lat2, lon2, alt2 = int(lat2b, 2), int(lon2b, 2), int(alt2b, 2)
# 131072 is 2^17, since CPR lat and lon are 17 bits each
cprlat_even, cprlon_even = lat1/131072.0, lon1/131072.0
cprlat_odd, cprlon_odd = lat2/131072.0, lon2/131072.0
print(cprlat_even, cprlon_even)
j = floor_(59*cprlat_even - 60*cprlat_odd)
print(j)
air_d_lat_even = 360.0 / 60
air_d_lat_odd = 360.0 / 59
# Lat
lat_even = float(air_d_lat_even * (j % 60 + cprlat_even))
lat_odd = float(air_d_lat_odd * (j % 59 + cprlat_odd))
if lat_even >= 270:
lat_even = lat_even - 360
if lat_odd >= 270:
lat_odd = lat_odd - 360
# Lon
ni = _cprN(lat_even, 0)
m = floor_(cprlon_even * (_cprNL(lat_even)-1) - cprlon_odd * _cprNL(lat_even) + 0.5)
lon = (360.0 / ni) * (m % ni + cprlon_even)
print("Lat", lat_even, "Lon", lon)
# Altitude
# Q-bit (bit 48) indicates whether the altitude is encoded in multiples of 25 or 100 ft (0: 100 ft, 1: 25 ft)
# The value can represent altitudes from -1000 to +50175 ft.
if alt1b[-5] == '1':
bits = alt1b[:-5] + alt1b[-4:]
n = int(bits, 2)
alt_ft = n*25 - 1000
print("Alt (ft)", alt_ft)
fs, data = wavfile.read("adsb_20190311_191728Z_1090000kHz_RF.wav")
T = 1/fs
print("Sample rate %f MS/s" % (fs / 1e6))
print("Cnt samples %d" % len(data))
print("Duration: %f s" % (T * len(data)))
data = data.astype(float)
cnt = data.shape[0]
# Processing only part on file (faster):
# cnt = 10000000
# data = data[:cnt]
print("Processing I/Q...")
I, Q = data[:, 0], data[:, 1]
A = np.sqrt(I*I + Q*Q)
bits = np.zeros(cnt)
# To see scope without any processing, uncomment
# plt.plot(A)
# plt.show()
# sys.exit(0)
print("Extracting signals...")
pos = 0
avg = 200
msg_start = 0
# Find beginning of each signal
while pos < cnt - 16*1024:
# P1 - message start
while pos < cnt - 16*1024:
if A[pos] < avg and A[pos+1] > avg and pos - msg_start > 1000:
msg_start = pos
bits[pos] = 100
pos += 4
break
pos += 1
start1, start2, start3, start4 = msg_start, 0, 0, 0
# P2
while pos < cnt - 16*1024:
if A[pos] < avg and A[pos+1] > avg:
start2 = pos
bits[pos] = 90
pos += 1
break
pos += 1
# P3
while pos < cnt - 16*1024:
if A[pos] < avg and A[pos+1] > avg:
start3 = pos
bits[pos] = 80
pos += 1
break
pos += 1
# P4
while pos < cnt - 16*1024:
if A[pos] < avg and A[pos+1] > avg:
start4 = pos
bits[pos] = 70
pos += 1
break
pos += 1
sig_diff = start4 - start1
if 20 < sig_diff < 25:
bits[msg_start] = 500
bit_len = int((start4 - start1) / 4.5)
# print(pos, start1, start4, ' - ', bit_len)
# start = start1 + 8*bit_len
parse_message(A, msg_start, bit_len)
pos += 450
# For debugging: check signal start
# plt.plot(A)
# plt.plot(bits)
# plt.show()

НадСюсь, ΠΊΠΎΠΌΡƒ-Ρ‚ΠΎ Π±Ρ‹Π»ΠΎ интСрСсно, спасибо Π·Π° Π²Π½ΠΈΠΌΠ°Π½ΠΈΠ΅.

Π˜ΡΡ‚ΠΎΡ‡Π½ΠΈΠΊ: habr.com