Flightradar24 - ahoana ny fandehany? Fizarana 2, protocole ADS-B

Salama Habr. Angamba izay rehetra nihaona na nahita havana na namana tao anaty fiaramanidina dia nampiasa ny serivisy Flightradar24 maimaim-poana. Ity dia fomba tena mety hanaraha-maso ny toeran'ny fiaramanidina amin'ny fotoana tena izy.

Flightradar24 - ahoana ny fandehany? Fizarana 2, protocole ADS-B

В tapany voalohany Nofaritana ny fitsipiky ny fiasan'ny serivisy an-tserasera toy izany. Handeha isika izao ary hamantatra izay angon-drakitra alefa sy azo avy amin'ny fiaramanidina mankany amin'ny tobim-pandraisana ary mamadika azy io amin'ny alàlan'ny Python.

История

Mazava ho azy fa tsy ampitaina ho hitan'ny mpampiasa amin'ny findainy ny angon-drakitra fiaramanidina. Ny rafitra dia antsoina hoe ADS-B (Automatic dependent surveillance-broadcast), ary ampiasaina handefasana ny vaovao momba ny fiaramanidina ho any amin'ny foibe fanaraha-maso - ny famantarana, ny fandrindrana, ny fitarihana, ny hafainganam-pandeha, ny haavony ary ny angona hafa dia alefa. Teo aloha, talohan'ny nahatongavan'ny rafitra toy izany, ny dispatcher dia tsy nahita afa-tsy teboka iray tamin'ny radara. Tsy ampy intsony izany rehefa be loatra ny fiaramanidina.

Ara-teknika, ADS-B dia ahitana mpanelanelana amin'ny fiaramanidina izay mandefa tsindraindray fonosana ny vaovao amin'ny somary avo matetika ny 1090 MHz (misy fomba hafa, fa tsy dia liana amin'izy ireo, satria ny fandrindrana ihany no nampitaina eto). Mazava ho azy fa ankoatry ny émetteur dia misy ihany koa ny resevera any ho any amin'ny seranam-piaramanidina, fa ho antsika mpampiasa dia mahaliana ny mpandray antsika.

Raha ny marina, raha ampitahaina, ny rafitra voalohany toy izany, Airnav Radarbox, natao ho an'ny mpampiasa tsotra, dia niseho tamin'ny 2007, ary mitentina $ 900 eo ho eo; ny famandrihana amin'ny serivisy tambajotra dia mitentina $ 250 isan-taona.

Flightradar24 - ahoana ny fandehany? Fizarana 2, protocole ADS-B

Ny hevitra momba ireo tompona Rosiana voalohany dia azo vakiana ao amin'ny forum radioscanner. Amin'izao fotoana izao dia lasa azo ampiasaina be dia be ny mpandray RTL-SDR, fitaovana mitovy amin'izany dia azo amboarina amin'ny $30; bebe kokoa momba izany dia ao amin'ny tapany voalohany. Andao hiroso amin'ny protocol mihitsy - hojerentsika ny fomba fiasa.

Mandray famantarana

Voalohany, mila raketina ny famantarana. Ny famantarana manontolo dia manana faharetan'ny 120 microsegondra fotsiny, noho izany dia ilaina ny mpandray SDR miaraka amin'ny fatran'ny santionany farafahakeliny 5 MHz, mba hanesorana ireo singa ao aminy.

Flightradar24 - ahoana ny fandehany? Fizarana 2, protocole ADS-B

Aorian'ny firaketana dia mahazo rakitra WAV izahay miaraka amin'ny santionany 5000000 santionany / seg; 30 segondra amin'ny fandraketana toy izany dia "lanja" eo amin'ny 500MB. Ny fihainoana azy miaraka amin'ny mpilalao haino aman-jery, mazava ho azy, dia tsy misy ilàna azy - tsy misy feo ny rakitra, fa mari-pamantarana onjam-peo mivantana - izany no fomba fiasan'ny Software Defined Radio.

Hanokatra sy hikarakara ilay rakitra amin'ny alàlan'ny Python izahay. Ireo izay te-hanandrana samirery dia afaka misintona rakitra ohatra rohy.

Aleo alaina ny rakitra dia hojerentsika izay ao anatiny.

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

Vokany: mahita “pulse” miharihary manoloana ny tabataba ambadika.

Flightradar24 - ahoana ny fandehany? Fizarana 2, protocole ADS-B

Ny "pulse" tsirairay dia famantarana, ny firafitra izay hita mazava tsara raha ampitomboinao ny fanapahan-kevitra eo amin'ny grafika.

Flightradar24 - ahoana ny fandehany? Fizarana 2, protocole ADS-B

Araka ny hitanao dia mifanaraka tsara amin'ny voalaza etsy ambony ny sary. Afaka manomboka manodina ny angona ianao.

Decoding

Voalohany dia mila maka rano kely ianao. Ny mari-pamantarana mihitsy dia voakodia amin'ny alàlan'ny famandrihana Manchester:

Flightradar24 - ahoana ny fandehany? Fizarana 2, protocole ADS-B

Avy amin'ny fahasamihafan'ny haavon'ny nibbles dia mora ny mahazo ny tena "0" sy "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"

Ny firafitry ny famantarana dia toy izao manaraka izao:

Flightradar24 - ahoana ny fandehany? Fizarana 2, protocole ADS-B

Andeha hojerentsika amin'ny antsipiriany ny saha.

DF (Format Downlink, 5 bit) - mamaritra ny karazana hafatra. Misy karazany maromaro:

Flightradar24 - ahoana ny fandehany? Fizarana 2, protocole ADS-B
(loharano latabatra)

Karazana DF17 ihany no mahaliana anay, satria... Io no ahitana ny coordinates ny fiaramanidina.

ICAO (24 bits) - kaody tokana iraisam-pirenena momba ny fiaramanidina. Azonao atao ny manamarina ny fiaramanidina amin'ny alàlan'ny kaody Online (indrisy fa najanon'ny mpanoratra ny fanavaozana ny angon-drakitra, fa mbola ilaina ihany). Ohatra, ho an'ny code 3c5ee2 dia manana ireto fampahalalana manaraka ireto izahay:

Flightradar24 - ahoana ny fandehany? Fizarana 2, protocole ADS-B

Ahitsio : in fanehoan-kevitra amin'ny lahatsoratra Ny famaritana ny kaody ICAO dia omena amin'ny antsipiriany bebe kokoa; Manoro hevitra aho mba hamaky azy ireo izay liana.

NY FANAZAVANA (56 na 112 bits) - ny tena angon-drakitra izay ho decode. Ny data 5 bit voalohany dia ny saha Karazana kaody, misy ny sobika amin'ny angona voatahiry (tsy afangaro amin'ny DF). Misy vitsivitsy amin'ireto karazany ireto:

Flightradar24 - ahoana ny fandehany? Fizarana 2, protocole ADS-B
(loharano latabatra)

Andeha isika hijery ohatra vitsivitsy momba ny fonosana.

Famantarana fiaramanidina

Ohatra amin'ny endrika binary:

00100 011 000101 010111 000111 110111 110001 111000

Data saha:

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

TC = 00100b = 4, ny tarehintsoratra C1-C8 tsirairay dia misy kaody mifanaraka amin'ny indices ao amin'ny tsipika:
#ABCDEFGHIJKLMNOPQRSTUVWXYZ######_################0123456789######

Amin'ny alàlan'ny famongorana ny tady dia mora ny mahazo ny kaody fiaramanidina: 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('#', ''))

Toetran'ny rivotra

Raha tsotra ny anarana dia sarotra kokoa ny coordinates. Izy ireo dia ampitaina amin'ny endrika 2, mitovy sy hafahafa. Kaody saha TC = 01011b = 11.

Flightradar24 - ahoana ny fandehany? Fizarana 2, protocole ADS-B

Ohatra amin'ny fonosana mitovy sy hafahafa:

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

Ny fikajiana ny koordinate dia mitranga araka ny fomba fiasa somary sarotra:

Flightradar24 - ahoana ny fandehany? Fizarana 2, protocole ADS-B
(loharano)

Tsy manam-pahaizana momba ny GIS aho, ka tsy fantatro hoe avy aiza izy io. Iza no mahalala, soraty ao amin'ny fanehoan-kevitra.

Ny haavony dia heverina ho tsotra kokoa - miankina amin'ny bitika manokana, dia azo aseho amin'ny endrika maromaro 25 na 100 metatra.

Velocity an'habakabaka

Package miaraka amin'ny TC=19. Ny mahaliana eto dia ny hafainganam-pandeha dia mety ho marina, raha oharina amin'ny tany (Ground Speed), na an'habakabaka, refesina amin'ny alàlan'ny sensor fiaramanidina (Airspeed). Ampitaina ihany koa ny sehatra maro samihafa:

Flightradar24 - ahoana ny fandehany? Fizarana 2, protocole ADS-B
(loharano)

famaranana

Araka ny hitanao, ny teknolojia ADS-B dia lasa symbiose mahaliana, raha ny fenitra dia mahasoa tsy ho an'ny matihanina ihany, fa koa ho an'ny mpampiasa tsotra. Saingy mazava ho azy, anjara lehibe amin'izany no nilalao ny teknolojia mora kokoa amin'ny mpandray SDR nomerika, izay ahafahan'ilay fitaovana mandray ara-bakiteny ireo famantarana miaraka amin'ny frequences mihoatra ny gigahertz "ho an'ny pennies."

Ao amin'ny fenitra mihitsy, mazava ho azy, misy zavatra maro hafa. Ireo liana dia afaka mijery ny PDF amin'ny pejy ICAO na tsidiho ilay efa voalaza etsy ambony tranonkala.

Tsy azo inoana fa hahasoa ny maro ireo voalaza etsy ambony ireo, fa farafaharatsiny ny hevitra ankapobeny momba ny fomba fiasa, manantena aho fa mijanona.

Raha ny marina, efa misy ny decoder efa vita amin'ny Python, azonao atao ny mianatra azy io eto. Ary ny tompon'ny mpandray SDR dia afaka mivory sy manangana décoder ADS-B efa vita avy amin'ny pejy, noresahina tamin'ny antsipiriany bebe kokoa izany tao amin'ny tapany voalohany.

Ny kaody loharanon'ny parser voalaza ao amin'ny lahatsoratra dia omena eo ambanin'ny fanapahana. Ity dia ohatra andrana izay tsy miseho ho famokarana, fa misy zavatra miasa ao, ary azo ampiasaina handinihana ny rakitra voarakitra etsy ambony.
Kaody loharano (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()

Manantena aho fa misy olona liana, misaotra amin'ny fiheveranao.

Source: www.habr.com

Add a comment