Flightradar24 - imagwira ntchito bwanji? Gawo 2, ADS-B protocol

Hello Habr. Mwina aliyense yemwe adakumanapo kapena kuwonana ndi achibale kapena abwenzi pandege wagwiritsa ntchito ntchito yaulere ya Flightradar24. Iyi ndi njira yabwino kwambiri yowonera momwe ndege ilili munthawi yeniyeni.

Flightradar24 - imagwira ntchito bwanji? Gawo 2, ADS-B protocol

В gawo loyamba Mfundo yoyendetsera ntchito yapaintaneti yotereyi idafotokozedwa. Tsopano tipita patsogolo ndikuwona zomwe zikutumizidwa ndikulandilidwa kuchokera ku ndege kupita kumalo olandirira ndikudzilemba tokha pogwiritsa ntchito Python.

История

Mwachiwonekere, deta ya ndege simatumizidwa kuti ogwiritsa ntchito awone pa mafoni awo. Dongosololi limatchedwa ADS-B (Automatic dependent surveillance-roadcast), ndipo imagwiritsidwa ntchito potumiza zidziwitso za ndege kupita kumalo owongolera - chizindikiritso chake, ma coordinates, mayendedwe, liwiro, kutalika ndi zina zambiri zimafalitsidwa. M'mbuyomu, zisanachitike machitidwe oterowo, wotumiza amangowona mfundo pa radar. Izi sizinali zokwanira pamene panali ndege zambiri.

Mwaukadaulo, ADS-B imakhala ndi chowulutsira pa ndege yomwe nthawi ndi nthawi imatumiza mapaketi azidziwitso pafupipafupi kwambiri 1090 MHz (pali mitundu ina, koma sitichita nawo chidwi, chifukwa kulumikizana kumangotumizidwa pano). Zachidziwikire, kuwonjezera pa chotumizira, palinso wolandila kwinakwake ku eyapoti, koma kwa ife, monga ogwiritsa ntchito, wolandila wathu ndi wosangalatsa.

Mwa njira, poyerekeza, dongosolo loyamba la Airnav Radarbox, lopangidwira ogwiritsa ntchito wamba, lidawonekera mu 2007, ndipo linagula pafupifupi $ 900; kulembetsa ku mautumiki a pa intaneti kumawononga $ 250 pachaka.

Flightradar24 - imagwira ntchito bwanji? Gawo 2, ADS-B protocol

Ndemanga za eni ake oyamba aku Russia zitha kuwerengedwa pabwaloli wailesi scanner. Tsopano popeza zolandila za RTL-SDR zapezeka ponseponse, chipangizo chofananira chitha kusonkhanitsidwa $30; zambiri za izi zinali mkati. gawo loyamba. Tiyeni tipitirire ku protocol yokha - tiyeni tiwone momwe imagwirira ntchito.

Kulandira zizindikiro

Choyamba, chizindikirocho chiyenera kulembedwa. Chizindikiro chonsecho chimakhala ndi nthawi ya ma microseconds a 120 okha, kotero kuti musungunuke bwino zigawo zake, cholandira cha SDR chokhala ndi mafupipafupi a 5 MHz ndichofunikira.

Flightradar24 - imagwira ntchito bwanji? Gawo 2, ADS-B protocol

Pambuyo kujambula, timalandira fayilo ya WAV yokhala ndi zitsanzo za 5000000 / sec; masekondi 30 a kujambula koteroko "kulemera" pafupifupi 500MB. Kumvetsera ndi wosewera mpira, ndithudi, n'kopanda ntchito - wapamwamba alibe phokoso, koma mwachindunji digitized wailesi chizindikiro - umu ndi mmene Software Defined Radio ntchito.

Titsegula ndikukonza fayiloyo pogwiritsa ntchito Python. Amene akufuna kuyesa okha akhoza kukopera chitsanzo kujambula kugwirizana.

Tiyeni titsitse fayiloyo ndikuwona zomwe zili mkati.

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

Zotsatira: tikuwona "ma pulse" momveka bwino motsutsana ndi phokoso lakumbuyo.

Flightradar24 - imagwira ntchito bwanji? Gawo 2, ADS-B protocol

"Kugunda" kulikonse ndi chizindikiro, mawonekedwe ake omwe amawoneka bwino ngati muwonjezera chigamulo pa graph.

Flightradar24 - imagwira ntchito bwanji? Gawo 2, ADS-B protocol

Monga mukuonera, chithunzicho chimagwirizana kwambiri ndi zomwe zaperekedwa m'mafotokozedwe pamwambapa. Mukhoza kuyamba kukonza deta.

Decoding

Choyamba, muyenera kupeza mtsinje pang'ono. Chizindikirocho chimasungidwa pogwiritsa ntchito encoding ya Manchester:

Flightradar24 - imagwira ntchito bwanji? Gawo 2, ADS-B protocol

Kuchokera pakusiyana kwa ma nibbles ndikosavuta kupeza zenizeni "0" ndi "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"

Kapangidwe ka chizindikiro palokha ndi motere:

Flightradar24 - imagwira ntchito bwanji? Gawo 2, ADS-B protocol

Tiyeni tione minda mwatsatanetsatane.

DF (Downlink Format, 5 bits) - imasankha mtundu wa uthenga. Pali mitundu ingapo:

Flightradar24 - imagwira ntchito bwanji? Gawo 2, ADS-B protocol
(gwero la tebulo)

Timangokonda mtundu wa DF17, chifukwa... Ndi izi zomwe zili ndi ma coordinates a ndege.

ICAO (24 bits) - nambala yapadera yapadziko lonse lapansi ya ndege. Mukhoza kuyang'ana ndege ndi code yake Online (mwatsoka, wolemba wasiya kukonzanso nkhokwe, koma idakali yofunika). Mwachitsanzo, pa code 3c5ee2 tili ndi izi:

Flightradar24 - imagwira ntchito bwanji? Gawo 2, ADS-B protocol

Sinthani: mu ndemanga ku nkhaniyo Kufotokozera kwa code ya ICAO kwaperekedwa mwatsatanetsatane; Ndikupangira kuti omwe ali ndi chidwi awerenge.

DETA (56 kapena 112 bits) - deta yeniyeni yomwe tidzasankha. Magawo 5 oyamba a data ndi gawo Lembani Khodi, yomwe ili ndi subtype ya deta yomwe ikusungidwa (kuti isasokonezedwe ndi DF). Pali angapo mwa mitundu iyi:

Flightradar24 - imagwira ntchito bwanji? Gawo 2, ADS-B protocol
(gwero la tebulo)

Tiyeni tiwone zitsanzo zingapo za phukusi.

Chizindikiritso cha ndege

Chitsanzo mu mawonekedwe a binary:

00100 011 000101 010111 000111 110111 110001 111000

Deta za data:

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

TC = 00100b = 4, chilembo chilichonse C1-C8 chili ndi zizindikiro zogwirizana ndi zizindikiro pamzerewu:
#ABCDEFGHIJKLMNOPQRSTUVWXYZ#####_##################0123456789#######

Polemba chingwe, ndikosavuta kupeza nambala yandege: 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('#', ''))

Malo apamlengalenga

Ngati dzinalo ndi losavuta, ndiye kuti zogwirizanitsa zimakhala zovuta kwambiri. Amafalitsidwa mu mawonekedwe a 2, ngakhale mafelemu osamvetseka. Field kodi TC = 01011b = 11.

Flightradar24 - imagwira ntchito bwanji? Gawo 2, ADS-B protocol

Chitsanzo cha paketi yofanana ndi yosamvetseka:

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

Kuwerengera kwa ma coordinates komweko kumachitika motsatira njira yovuta kwambiri:

Flightradar24 - imagwira ntchito bwanji? Gawo 2, ADS-B protocol
(gwero)

Sindine katswiri wa GIS, kotero sindikudziwa komwe akuchokera. Ndani akudziwa, lembani mu ndemanga.

Kutalika kumaonedwa kuti ndi kosavuta - kutengera pang'ono, kumatha kuimiridwa ngati kuchulukitsa kwa 25 kapena 100 mapazi.

Kuthamanga kwa Airborne

Phukusi lokhala ndi TC = 19. Chochititsa chidwi apa ndi chakuti liwiro likhoza kukhala lolondola, logwirizana ndi nthaka (Ground Speed), kapena ndege, yoyesedwa ndi sensa ya ndege (Airspeed). Magawo ambiri osiyanasiyana amafalitsidwanso:

Flightradar24 - imagwira ntchito bwanji? Gawo 2, ADS-B protocol
(gwero)

Pomaliza

Monga mukuonera, teknoloji ya ADS-B yakhala symbiosis yosangalatsa, pamene muyeso ndiwothandiza osati kwa akatswiri okha, komanso kwa ogwiritsa ntchito wamba. Koma zowonadi, gawo lalikulu pa izi lidaseweredwa ndi ukadaulo wotsika mtengo wa olandila digito wa SDR, omwe amalola chipangizocho kuti chilandire ma siginecha ndi ma frequency pamwamba pa gigahertz "pamakobiri."

Muyeso womwewo, ndithudi, pali zambiri. Amene ali ndi chidwi akhoza kuona PDF patsamba ICAO kapena pitani ku zomwe tazitchula kale pamwambapa webusaitiyi.

Ndizokayikitsa kuti zonse zomwe tafotokozazi zitha kukhala zothandiza kwa ambiri, koma lingaliro la momwe zimagwirira ntchito, ndikhulupirira, likhalabe.

Mwa njira, decoder yopangidwa kale ku Python ilipo kale, mutha kuiphunzira apa. Ndipo eni ake olandila a SDR amatha kusonkhana ndikuyambitsa chotsitsa cha ADS-B chokonzeka kuchokera patsamba, izi zidakambidwa mwatsatanetsatane mu gawo loyamba.

Magwero a wofotokozera omwe akufotokozedwa m'nkhaniyi aperekedwa pansipa. Ichi ndi chitsanzo choyesera chomwe sichimayesa kupanga, koma zinthu zina zimagwira ntchito, ndipo chitha kugwiritsidwa ntchito kusanthula fayilo yomwe yalembedwa pamwambapa.
Gwero kodi (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()

Ndikukhulupirira kuti wina anali ndi chidwi, zikomo chifukwa cha chidwi chanu.

Source: www.habr.com

Kuwonjezera ndemanga