Flightradar24 - kedu ka ọ si arụ ọrụ? Nkeji 2, ADS-B Protocol

Ndewo Habr. Eleghị anya, onye ọ bụla hụtụrụla ma ọ bụ hụ na ndị ikwu ma ọ bụ ndị enyi nọ n'ụgbọelu ejirila ọrụ Flightradar24 n'efu. Nke a bụ ụzọ dị mma iji soro ọnọdụ ụgbọ elu ahụ ozugbo.

Flightradar24 - kedu ka ọ si arụ ọrụ? Nkeji 2, ADS-B Protocol

В akụkụ mbụ A kọwara ụkpụrụ ọrụ nke ọrụ ịntanetị dị otú ahụ. Ugbu a, anyị ga-aga n'ihu ma chọpụta ihe data a na-eziga na natara site na ụgbọ elu gaa na ọdụ nnata ma depụta ya n'onwe anyị site na iji Python.

История

N'ụzọ doro anya, a naghị ebufe data ụgbọ elu maka ndị ọrụ ịhụ na ekwentị ha. A na-akpọ usoro a ADS-B (Automatic dabere onyunyo-mgbasa ozi), na-eji na-akpaghị aka na-ebufe ozi gbasara ụgbọ elu na ebe njikwa - njirimara ya, nhazi, ntụziaka, ọsọ, elu na data ndị ọzọ. Na mbụ, tupu ọbịbịa nke usoro ndị dị otú ahụ, onye na-ezipụ ozi nwere ike ịhụ naanị otu isi na radar. Nke a ezughịkwa mgbe enwere ọtụtụ ụgbọ elu.

Teknụzụ, ADS-B nwere onye na-ebugharị na ụgbọ elu nke na-eziga ngwugwu ozi kwa oge n'ogo dị elu nke 1090 MHz (enwere ụdịdị ndị ọzọ, mana anyị enweghị mmasị na ha, ebe ọ bụ na a na-ebunye nhazi naanị ebe a). N'ezie, na mgbakwunye na onye na-ebugharị, e nwekwara onye na-anata ebe n'ọdụ ụgbọ elu, ma maka anyị, dị ka ndị ọrụ, onye na-anata onwe anyị na-adọrọ mmasị.

Site n'ụzọ, maka ntụnyere, usoro mbụ dị otú ahụ, Airnav Radarbox, nke e mere maka ndị ọrụ nkịtị, pụtara na 2007, na-efu ihe dị ka $900; ndenye aha na ọrụ netwọk na-efu $ 250 ọzọ kwa afọ.

Flightradar24 - kedu ka ọ si arụ ọrụ? Nkeji 2, ADS-B Protocol

Enwere ike ịgụ nyocha nke ndị nwe Russia mbụ na forum nyocha redio. Ugbu a ndị na-anata RTL-SDR adịla ebe niile, enwere ike ikpokọta ngwaọrụ yiri ya maka $30; ihe gbasara nke a dị na akụkụ mbụ. Ka anyị gaa n'ihu na protocol n'onwe ya - ka anyị hụ ka o si arụ ọrụ.

Ịnata akara

Nke mbụ, ọ dị mkpa ka edekọ mgbaàmà ahụ. Mgbama ahụ dum nwere ogologo oge nke naanị 120 microseconds, yabụ iji kesaa ihe ndị mejupụtara ya nke ọma, onye na-anata SDR nwere oge nlele nke opekata mpe 5 MHz bụ ihe na-achọsi ike.

Flightradar24 - kedu ka ọ si arụ ọrụ? Nkeji 2, ADS-B Protocol

Mgbe ndekọ, anyị na-enweta a WAV faịlụ na a sample ọnụego nke 5000000 samples / sk; 30 sekọnd nke ndekọ dị otú ahụ "na-atụ" banyere 500MB. Iji ihe ọkpụkpọ mgbasa ozi na-ege ya ntị, n'ezie, abaghị uru - faịlụ ahụ enweghị ụda, mana akara redio digitized ozugbo - otu a ka Software Defined Radio si arụ ọrụ.

Anyị ga-emepe ma hazie faịlụ ahụ site na iji Python. Ndị chọrọ ịnwale n'onwe ha nwere ike budata ndekọ ndekọ ihe atụ njikọ.

Ka anyị budata faịlụ ahụ wee hụ ihe dị n'ime.

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

Nsonaazụ: anyị na-ahụ "pulses" doro anya megide mkpọtụ ndabere.

Flightradar24 - kedu ka ọ si arụ ọrụ? Nkeji 2, ADS-B Protocol

Onye ọ bụla "pulse" bụ ihe mgbaàmà, usoro nke a na-ahụ anya nke ọma ma ọ bụrụ na ị na-abawanye mkpebi na eserese ahụ.

Flightradar24 - kedu ka ọ si arụ ọrụ? Nkeji 2, ADS-B Protocol

Dị ka ị na-ahụ, foto a dabara nke ọma na ihe e nyere na nkọwa dị n'elu. Ị nwere ike ịmalite nhazi data.

Ịmepụta koodu

Nke mbụ, ịkwesịrị ịnweta ntakịrị iyi. A na-etinye akara ngosi ahụ n'onwe ya site na iji koodu Manchester:

Flightradar24 - kedu ka ọ si arụ ọrụ? Nkeji 2, ADS-B Protocol

Site na ọkwa dị iche na nibbles ọ dị mfe ịnweta ezigbo "0" na "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"

Ọdịdị nke mgbaàmà n'onwe ya bụ nke a:

Flightradar24 - kedu ka ọ si arụ ọrụ? Nkeji 2, ADS-B Protocol

Ka anyị leba anya n'ubi ahụ n'ụzọ zuru ezu.

DF (Downlink Format, 5 bits) - na-ekpebi ụdị ozi. Enwere ọtụtụ ụdị:

Flightradar24 - kedu ka ọ si arụ ọrụ? Nkeji 2, ADS-B Protocol
(isi iyi okpokoro)

Anyị nwere mmasị na ụdị DF17, n'ihi na ... Ọ bụ nke a nwere nhazi nke ụgbọ elu.

ICAO (24 bits) - koodu pụrụ iche nke mba ụwa. Ị nwere ike ịlele ụgbọ elu site na koodu ya online (n'ụzọ dị mwute, onye edemede akwụsịla imelite nchekwa data, ma ọ ka dị mkpa). Dịka ọmụmaatụ, maka koodu 3c5ee2 anyị nwere ozi ndị a:

Flightradar24 - kedu ka ọ si arụ ọrụ? Nkeji 2, ADS-B Protocol

Dezie: in kwuru na akụkọ Enyere nkọwa nke koodu ICAO n'ụzọ zuru ezu; Ana m akwado ka ndị nwere mmasị gụọ ya.

data (56 ma ọ bụ 112 ibe n'ibe) - ezigbo data anyị ga-achọpụta. Nke mbụ 5 bit nke data bụ ubi Codedị Koodu, nwere subtype nke data a na-echekwa (ka DF ghara mgbagwoju anya). Enwere ole na ole n'ime ụdị ndị a:

Flightradar24 - kedu ka ọ si arụ ọrụ? Nkeji 2, ADS-B Protocol
(isi iyi okpokoro)

Ka anyị lee ihe atụ ole na ole nke ngwugwu.

njirimara ụgbọ elu

Ọmụmaatụ n'ụdị ọnụọgụ abụọ:

00100 011 000101 010111 000111 110111 110001 111000

Ebe data:

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

TC = 00100b = 4, agwa ọ bụla C1-C8 nwere koodu kwekọrọ na indices na ahịrị:
#ABCDEFGHIJKLMNOPQRSTUVWXYZ#######################0123456789#####

Site na imezi eriri ahụ, ọ dị mfe ịnweta koodu ụgbọ elu: 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('#', ''))

Ọnọdụ ikuku

Ọ bụrụ na aha ahụ dị mfe, mgbe ahụ, nhazi ndị ọzọ dị mgbagwoju anya. A na-ebunye ha n'ụdị nke 2, ọbụna na okpokolo agba na-adịghị mma. Koodu mpaghara TC = 01011b = 11.

Flightradar24 - kedu ka ọ si arụ ọrụ? Nkeji 2, ADS-B Protocol

Ọmụmaatụ nke ngwungwu ọbụna na ndị na-adịghị mma:

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

Mgbakọ nke nhazi n'onwe ya na-eme dịka usoro aghụghọ siri dị:

Flightradar24 - kedu ka ọ si arụ ọrụ? Nkeji 2, ADS-B Protocol
(isi iyi)

Abụghị m ọkachamara GIS, yabụ na amaghị m ebe o si abịa. Onye maara, dee na nkọwa.

A na-ewere ịdị elu dị mfe - dabere na ntakịrị ntakịrị, enwere ike ịnọchite anya ya dị ka otutu nke 25 ma ọ bụ 100 ụkwụ.

Ọsọ ikuku

Ngwugwu ya na TC=19. Ihe na-adọrọ mmasị ebe a bụ na ọsọ nwere ike ịbụ nke ziri ezi, n'ihe gbasara ala (Ground Speed), ma ọ bụ ikuku, nke ihe mmetụta ụgbọ elu (Airspeed) tụrụ atụ. A na-ekesakwa ọtụtụ mpaghara dị iche iche:

Flightradar24 - kedu ka ọ si arụ ọrụ? Nkeji 2, ADS-B Protocol
(isi iyi)

nkwubi

Dị ka ị pụrụ ịhụ, ADS-B technology aghọwo ihe na-akpali symbiosis, mgbe ọkọlọtọ bara uru ọ bụghị nanị na ndị ọkachamara, kamakwa ndị ọrụ nkịtị. Mana n'ezie, ọrụ dị mkpa na nke a bụ teknụzụ dị ọnụ ala nke ndị na-anata SDR dijitalụ, nke na-enye ohere ka ngwaọrụ ahụ nweta akara n'ụzọ nkịtị na ugboro ugboro karịa gigahertz “maka pennies.”

Na ọkọlọtọ n'onwe ya, n'ezie, e nwere ọtụtụ ihe ndị ọzọ. Ndị nwere mmasị nwere ike ịlele PDF na ibe ICAO ma ọ bụ gaa na nke a kpọtụrụ aha n'elu mkpokọta.

O yighị ka ihe niile a dị n'elu ga-aba uru nye ọtụtụ ndị, ma ọ dịkarịa ala, echiche izugbe nke otú o si arụ ọrụ, m na-atụ anya, na-anọgide.

Site n'ụzọ, ihe ndozi emebere na Python adịlarị, ị nwere ike ịmụ ya ebe a. Ndị nwe ndị na-anata SDR nwere ike ịgbakọta ma malite ihe ndozi ADS-B emebere site na ibe, A tụlere nke a n'ụzọ zuru ezu na akụkụ mbụ.

Enyere koodu isi mmalite nke parser nke akọwara n'isiokwu a n'okpuru ịkpụ. Nke a bụ ihe atụ nnwale nke na-adịghị eme ka ọ bụ mmepụta, mana ụfọdụ ihe na-arụ ọrụ na ya, enwere ike iji ya mee ka faịlụ ahụ dekọọ n'elu.
Koodu isi mmalite (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()

Enwere m olileanya na mmadụ nwere mmasị, daalụ maka nlebara anya gị.

isi: www.habr.com

Tinye a comment