Flightradar24 - ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋‚˜์š”? 2๋ถ€, ADS-B ํ”„๋กœํ† ์ฝœ

์•ˆ๋…•ํ•˜์„ธ์š” ํ•˜๋ธŒ๋ฅด์ž…๋‹ˆ๋‹ค. ์•„๋งˆ๋„ ๋น„ํ–‰๊ธฐ์—์„œ ์นœ์ฒ™์ด๋‚˜ ์นœ๊ตฌ๋ฅผ ๋งŒ๋‚˜๊ฑฐ๋‚˜ ๋ฐฐ์›…ํ•ด ๋ณธ ์ ์ด ์žˆ๋Š” ๋ชจ๋“  ์‚ฌ๋žŒ์€ ๋ฌด๋ฃŒ Flightradar24 ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•ด ๋ณธ ์ ์ด ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋Š” ์‹ค์‹œ๊ฐ„์œผ๋กœ ํ•ญ๊ณต๊ธฐ์˜ ์œ„์น˜๋ฅผ โ€‹โ€‹์ถ”์ ํ•˜๋Š” ๋งค์šฐ ํŽธ๋ฆฌํ•œ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

Flightradar24 - ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋‚˜์š”? 2๋ถ€, ADS-B ํ”„๋กœํ† ์ฝœ

ะ’ ์ฒซ ๋ฒˆ์งธ ๋ถ€๋ถ„ ์ด๋Ÿฌํ•œ ์˜จ๋ผ์ธ ์„œ๋น„์Šค์˜ ์šด์˜ ์›๋ฆฌ๋ฅผ ์„ค๋ช…ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด์ œ ํ•ญ๊ณต๊ธฐ์—์„œ ์ˆ˜์‹ ๊ตญ์œผ๋กœ ์–ด๋–ค ๋ฐ์ดํ„ฐ๊ฐ€ ์†ก์ˆ˜์‹ ๋˜๋Š”์ง€ ํŒŒ์•…ํ•˜๊ณ  Python์„ ์‚ฌ์šฉํ•˜์—ฌ ์ง์ ‘ ๋””์ฝ”๋”ฉํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

์ด์•ผ๊ธฐ

๋‹น์—ฐํžˆ ํ•ญ๊ณต๊ธฐ ๋ฐ์ดํ„ฐ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์Šค๋งˆํŠธํฐ์—์„œ ๋ณผ ์ˆ˜ ์žˆ๋„๋ก ์ „์†ก๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด ์‹œ์Šคํ…œ์€ ADS-B(์ž๋™ ์ข…์† ๊ฐ์‹œ - ๋ฐฉ์†ก)๋ผ๊ณ  ํ•˜๋ฉฐ ํ•ญ๊ณต๊ธฐ์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ๊ด€์ œ ์„ผํ„ฐ์— ์ž๋™์œผ๋กœ ์ „์†กํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ์ฆ‰, ์‹๋ณ„์ž, ์ขŒํ‘œ, ๋ฐฉํ–ฅ, ์†๋„, ๊ณ ๋„ ๋ฐ ๊ธฐํƒ€ ๋ฐ์ดํ„ฐ๊ฐ€ ์ „์†ก๋ฉ๋‹ˆ๋‹ค. ์ด์ „์—๋Š” ์ด๋Ÿฌํ•œ ์‹œ์Šคํ…œ์ด ์ถœํ˜„ํ•˜๊ธฐ ์ „์—๋Š” ํŒŒ๊ฒฌ์ž๊ฐ€ ๋ ˆ์ด๋”์˜ ํ•œ ์ง€์ ๋งŒ ๋ณผ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๋น„ํ–‰๊ธฐ๊ฐ€ ๋„ˆ๋ฌด ๋งŽ์œผ๋ฉด ๋” ์ด์ƒ ์ถฉ๋ถ„ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

๊ธฐ์ˆ ์ ์œผ๋กœ ADS-B๋Š” 1090MHz์˜ ์ƒ๋‹นํžˆ ๋†’์€ ์ฃผํŒŒ์ˆ˜์—์„œ ์ •๋ณด ํŒจํ‚ท์„ ์ฃผ๊ธฐ์ ์œผ๋กœ ์ „์†กํ•˜๋Š” ํ•ญ๊ณต๊ธฐ์˜ ์†ก์‹ ๊ธฐ๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค(๋‹ค๋ฅธ ๋ชจ๋“œ๋„ ์žˆ์ง€๋งŒ ์ขŒํ‘œ๋Š” ์—ฌ๊ธฐ์—์„œ๋งŒ ์ „์†ก๋˜๊ธฐ ๋•Œ๋ฌธ์— ๊ทธ๋‹ค์ง€ ๊ด€์‹ฌ์ด ์—†์Šต๋‹ˆ๋‹ค). ๋ฌผ๋ก  ์†ก์‹ ๊ธฐ โ€‹โ€‹์™ธ์— ๊ณตํ•ญ ์–ด๋”˜๊ฐ€์— ์ˆ˜์‹ ๊ธฐ๋„ ์žˆ์ง€๋งŒ ์‚ฌ์šฉ์ž์ธ ์šฐ๋ฆฌ์—๊ฒŒ๋Š” ์ž์ฒด ์ˆ˜์‹ ๊ธฐ๊ฐ€ ํฅ๋ฏธ๋กญ์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฐ๋ฐ ๋น„๊ต๋ฅผ ์œ„ํ•ด ์ผ๋ฐ˜ ์‚ฌ์šฉ์ž๋ฅผ ์œ„ํ•ด ์„ค๊ณ„๋œ ์ตœ์ดˆ์˜ ์‹œ์Šคํ…œ์ธ Airnav Radarbox๊ฐ€ 2007๋…„์— ์ถœ์‹œ๋˜์—ˆ์œผ๋ฉฐ ๊ฐ€๊ฒฉ์€ ์•ฝ 900๋‹ฌ๋Ÿฌ์ด๋ฉฐ, ๋„คํŠธ์›Œํฌ ์„œ๋น„์Šค ๊ฐ€์ž… ๋น„์šฉ์€ ์—ฐ๊ฐ„ 250๋‹ฌ๋Ÿฌ์ž…๋‹ˆ๋‹ค.

Flightradar24 - ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋‚˜์š”? 2๋ถ€, ADS-B ํ”„๋กœํ† ์ฝœ

์ตœ์ดˆ์˜ ๋Ÿฌ์‹œ์•„ ์†Œ์œ ์ž์— ๋Œ€ํ•œ ๋ฆฌ๋ทฐ๋Š” ํฌ๋Ÿผ์—์„œ ์ฝ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ผ๋””์˜ค ์Šค์บ๋„ˆ. ์ด์ œ RTL-SDR ์ˆ˜์‹ ๊ธฐ๊ฐ€ ๋„๋ฆฌ ๋ณด๊ธ‰๋˜์—ˆ์œผ๋ฏ€๋กœ ์œ ์‚ฌํ•œ ์žฅ์น˜๋ฅผ 30๋‹ฌ๋Ÿฌ์— ์กฐ๋ฆฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์ฒซ ๋ฒˆ์งธ ๋ถ€๋ถ„. ํ”„๋กœํ† ์ฝœ ์ž์ฒด๋กœ ๋„˜์–ด๊ฐ€์„œ ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์‹ ํ˜ธ ์ˆ˜์‹ 

๋จผ์ € ์‹ ํ˜ธ๋ฅผ ๋…น์Œํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ „์ฒด ์‹ ํ˜ธ์˜ ์ง€์† ์‹œ๊ฐ„์€ 120๋งˆ์ดํฌ๋กœ์ดˆ์— ๋ถˆ๊ณผํ•˜๋ฏ€๋กœ ํ•ด๋‹น ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ํŽธ์•ˆํ•˜๊ฒŒ ๋ถ„ํ•ดํ•˜๋ ค๋ฉด ์ƒ˜ํ”Œ๋ง ์ฃผํŒŒ์ˆ˜๊ฐ€ 5MHz ์ด์ƒ์ธ SDR ์ˆ˜์‹ ๊ธฐ๊ฐ€ ๋ฐ”๋žŒ์งํ•ฉ๋‹ˆ๋‹ค.

Flightradar24 - ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋‚˜์š”? 2๋ถ€, ADS-B ํ”„๋กœํ† ์ฝœ

๋…น์Œ ํ›„์— ์šฐ๋ฆฌ๋Š” ์ƒ˜ํ”Œ๋ง ์†๋„๊ฐ€ 5000000 ์ƒ˜ํ”Œ/์ดˆ์ธ WAV ํŒŒ์ผ์„ ์ˆ˜์‹ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋…น์Œ์˜ 30์ดˆ ๋ถ„๋Ÿ‰์€ ์•ฝ 500MB์ž…๋‹ˆ๋‹ค. ๋ฌผ๋ก  ๋ฏธ๋””์–ด ํ”Œ๋ ˆ์ด์–ด๋กœ ๋“ฃ๋Š” ๊ฒƒ์€ ์“ธ๋ชจ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ํŒŒ์ผ์—๋Š” ์‚ฌ์šด๋“œ๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์ง€ ์•Š์ง€๋งŒ ์ง์ ‘ ๋””์ง€ํ„ธํ™”๋œ ๋ผ๋””์˜ค ์‹ ํ˜ธ๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ๋ฐ”๋กœ ์†Œํ”„ํŠธ์›จ์–ด ์ •์˜ ๋ผ๋””์˜ค๊ฐ€ ์ž‘๋™ํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.

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 ํ”„๋กœํ† ์ฝœ

๋ณด์‹œ๋‹ค์‹œํ”ผ, ๊ทธ๋ฆผ์€ ์œ„์˜ ์„ค๋ช…์— ์ œ๊ณต๋œ ๋‚ด์šฉ๊ณผ ์ƒ๋‹นํžˆ ์ผ์น˜ํ•ฉ๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ๋ฅผ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋””์ฝ”๋”ฉ

๋จผ์ € ๋น„ํŠธ ์ŠคํŠธ๋ฆผ์„ ์–ป์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์‹ ํ˜ธ ์ž์ฒด๋Š” ๋งจ์ฒด์Šคํ„ฐ ์ธ์ฝ”๋”ฉ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ธ์ฝ”๋”ฉ๋ฉ๋‹ˆ๋‹ค.

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 (๋‹ค์šด๋งํฌ ํ˜•์‹, 5๋น„ํŠธ) - ๋ฉ”์‹œ์ง€ ์œ ํ˜•์„ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ์œ ํ˜•์ด ์žˆ์Šต๋‹ˆ๋‹ค:

Flightradar24 - ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋‚˜์š”? 2๋ถ€, ADS-B ํ”„๋กœํ† ์ฝœ
(ํ…Œ์ด๋ธ” ์†Œ์Šค)

์šฐ๋ฆฌ๋Š” DF17 ์œ ํ˜•์—๋งŒ ๊ด€์‹ฌ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด... ์ด๊ฒƒ์€ ํ•ญ๊ณต๊ธฐ์˜ ์ขŒํ‘œ๋ฅผ ํฌํ•จํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

ICAO (24๋น„ํŠธ) - ํ•ญ๊ณต๊ธฐ์˜ ๊ตญ์ œ ๊ณ ์œ  ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. ์ฝ”๋“œ๋กœ ๋น„ํ–‰๊ธฐ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค ์˜จ๋ผ์ธ์œผ๋กœ (๋ถˆํ–‰ํžˆ๋„ ์ €์ž๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—…๋ฐ์ดํŠธ๋ฅผ ์ค‘๋‹จํ–ˆ์ง€๋งŒ ์—ฌ์ „ํžˆ ๊ด€๋ จ์ด ์žˆ์Šต๋‹ˆ๋‹ค). ์˜ˆ๋ฅผ ๋“ค์–ด ์ฝ”๋“œ 3c5ee2์˜ ๊ฒฝ์šฐ ๋‹ค์Œ ์ •๋ณด๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

Flightradar24 - ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋‚˜์š”? 2๋ถ€, ADS-B ํ”„๋กœํ† ์ฝœ

ํŽธ์ง‘ : ์—์„œ ๊ธฐ์‚ฌ์— ๋Œ€ํ•œ ์˜๊ฒฌ ICAO ์ฝ”๋“œ์— ๋Œ€ํ•œ ์„ค๋ช…์ด ๋” ์ž์„ธํžˆ ๋‚˜์™€ ์žˆ์œผ๋‹ˆ ๊ด€์‹ฌ ์žˆ๋Š” ๋ถ„๋“ค์€ ๊ผญ ์ฝ์–ด๋ณด์‹œ๊ธธ ๊ถŒํ•ฉ๋‹ˆ๋‹ค.

๋ฐ์ดํ„ฐ (56 ๋˜๋Š” 112๋น„ํŠธ) - ๋””์ฝ”๋”ฉํ•  ์‹ค์ œ ๋ฐ์ดํ„ฐ์ž…๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ์˜ ์ฒ˜์Œ 5๋น„ํŠธ๋Š” ํ•„๋“œ์ž…๋‹ˆ๋‹ค. ์œ ํ˜• ์ฝ”๋“œ, ์ €์žฅ๋˜๋Š” ๋ฐ์ดํ„ฐ์˜ ํ•˜์œ„ ์œ ํ˜•์„ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค(DF์™€ ํ˜ผ๋™ํ•˜์ง€ ๋งˆ์„ธ์š”). ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์œ ํ˜•์ด ๋งŽ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

Flightradar24 - ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋‚˜์š”? 2๋ถ€, ADS-B ํ”„๋กœํ† ์ฝœ
(ํ…Œ์ด๋ธ” ์†Œ์Šค)

ํŒจํ‚ค์ง€์˜ ๋ช‡ ๊ฐ€์ง€ ์˜ˆ๋ฅผ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

ํ•ญ๊ณต๊ธฐ ์‹๋ณ„

๋ฐ”์ด๋„ˆ๋ฆฌ ํ˜•์‹์˜ ์˜ˆ:

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('#', ''))

๊ณต์ˆ˜ ์œ„์น˜

์ด๋ฆ„์ด ๋‹จ์ˆœํ•˜๋ฉด ์ขŒํ‘œ๊ฐ€ ๋” ๋ณต์žกํ•ด์ง‘๋‹ˆ๋‹ค. 2๊ฐœ์˜ ์ง์ˆ˜ ๋ฐ ํ™€์ˆ˜ ํ”„๋ ˆ์ž„ ํ˜•ํƒœ๋กœ ์ „์†ก๋ฉ๋‹ˆ๋‹ค. ํ•„๋“œ ์ฝ”๋“œ TC = 01011b = 11.

Flightradar24 - ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋‚˜์š”? 2๋ถ€, ADS-B ํ”„๋กœํ† ์ฝœ

์ง์ˆ˜ ๋ฐ ํ™€์ˆ˜ ํŒจํ‚ท์˜ ์˜ˆ:

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

์ขŒํ‘œ ์ž์ฒด์˜ ๊ณ„์‚ฐ์€ ๋‹ค์†Œ ๊นŒ๋‹ค๋กœ์šด ๊ณต์‹์— ๋”ฐ๋ผ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

Flightradar24 - ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋‚˜์š”? 2๋ถ€, ADS-B ํ”„๋กœํ† ์ฝœ
(์ถœ์ฒ˜)

๋‚˜๋Š” GIS ์ „๋ฌธ๊ฐ€๊ฐ€ ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ทธ๊ฒƒ์ด ์–ด๋””์„œ ๋‚˜์˜ค๋Š”์ง€ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค. ์•„์‹œ๋Š” ๋ถ„ ๋Œ“๊ธ€์— ์ ์–ด์ฃผ์„ธ์š”.

๋†’์ด๋Š” ๋” ๊ฐ„๋‹จํ•œ ๊ฒƒ์œผ๋กœ ๊ฐ„์ฃผ๋ฉ๋‹ˆ๋‹ค. ํŠน์ • ๋น„ํŠธ์— ๋”ฐ๋ผ 25ํ”ผํŠธ ๋˜๋Š” 100ํ”ผํŠธ์˜ ๋ฐฐ์ˆ˜๋กœ ํ‘œ์‹œ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ณต์ˆ˜ ์†๋„

TC=19์ธ ํŒจํ‚ค์ง€. ์—ฌ๊ธฐ์„œ ํฅ๋ฏธ๋กœ์šด ์ ์€ ์†๋„๊ฐ€ ์ง€๋ฉด์„ ๊ธฐ์ค€์œผ๋กœ ์ •ํ™•ํ•  ์ˆ˜๋„ ์žˆ๊ณ (Ground Speed) ํ•ญ๊ณต๊ธฐ ์„ผ์„œ๋กœ ์ธก์ •๋œ ๊ณต์ค‘ ์†๋„(Airspeed)์ผ ์ˆ˜๋„ ์žˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋‹ค์–‘ํ•œ ํ•„๋“œ๋„ ์ „์†ก๋ฉ๋‹ˆ๋‹ค.

Flightradar24 - ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋‚˜์š”? 2๋ถ€, ADS-B ํ”„๋กœํ† ์ฝœ
(์ถœ์ฒ˜)

๊ฒฐ๋ก 

๋ณด์‹œ๋‹ค์‹œํ”ผ ADS-B ๊ธฐ์ˆ ์€ ํ‘œ์ค€์ด ์ „๋ฌธ๊ฐ€๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ผ๋ฐ˜ ์‚ฌ์šฉ์ž์—๊ฒŒ๋„ ์œ ์šฉํ•  ๋•Œ ํฅ๋ฏธ๋กœ์šด ๊ณต์ƒ ๊ด€๊ณ„๊ฐ€ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋ฌผ๋ก  ์—ฌ๊ธฐ์„œ ์ค‘์š”ํ•œ ์—ญํ• ์€ ์ €๋ ดํ•œ ๋””์ง€ํ„ธ SDR ์ˆ˜์‹ ๊ธฐ ๊ธฐ์ˆ ์— ์˜ํ•ด ์ˆ˜ํ–‰๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์žฅ์น˜๋Š” ๋ฌธ์ž ๊ทธ๋Œ€๋กœ "XNUMX์„ผํŠธ์—" ๊ธฐ๊ฐ€ํ—ค๋ฅด์ธ  ์ด์ƒ์˜ ์ฃผํŒŒ์ˆ˜๋กœ ์‹ ํ˜ธ๋ฅผ ์ˆ˜์‹ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฌผ๋ก  ํ‘œ์ค€ ์ž์ฒด์—๋Š” ํ›จ์”ฌ ๋” ๋งŽ์€ ๊ฒƒ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๊ด€์‹ฌ ์žˆ๋Š” ์‚ฌ๋žŒ์€ ํ•ด๋‹น ํŽ˜์ด์ง€์—์„œ PDF๋ฅผ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ICAO ๋˜๋Š” ์œ„์—์„œ ์ด๋ฏธ ์–ธ๊ธ‰ํ•œ ๊ณณ์„ ๋ฐฉ๋ฌธํ•˜์„ธ์š”. ์›น ์‚ฌ์ดํŠธ.

์œ„์˜ ๋ชจ๋“  ๋‚ด์šฉ์ด ๋งŽ์€ ์‚ฌ๋žŒ๋“ค์—๊ฒŒ ์œ ์šฉํ•  ๊ฒƒ ๊ฐ™์ง€๋Š” ์•Š์ง€๋งŒ ์ ์–ด๋„ ์ž‘๋™ ๋ฐฉ์‹์— ๋Œ€ํ•œ ์ผ๋ฐ˜์ ์ธ ์•„์ด๋””์–ด๋Š” ๋‚จ์•„ ์žˆ๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค.

๊ทธ๊ฑด ๊ทธ๋ ‡๊ณ , ๊ธฐ์„ฑ Python ๋””์ฝ”๋”๊ฐ€ ์ด๋ฏธ ์กด์žฌํ•˜๋ฏ€๋กœ ์—ฐ๊ตฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—. SDR ์ˆ˜์‹ ๊ธฐ ์†Œ์œ ์ž๋Š” ๊ธฐ์„ฑํ’ˆ ADS-B ๋””์ฝ”๋”๋ฅผ ์กฐ๋ฆฝํ•˜๊ณ  ์ถœ์‹œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํŽ˜์ด์ง€์—์„œ, ์ด๋Š” ์—์„œ ๋” ์ž์„ธํžˆ ๋…ผ์˜๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ฒซ ๋ฒˆ์งธ ๋ถ€๋ถ„.

๊ธฐ์‚ฌ์— ์„ค๋ช…๋œ ํŒŒ์„œ์˜ ์†Œ์Šค ์ฝ”๋“œ๋Š” ์ปท ์•„๋ž˜์— ๋‚˜์™€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ํ”„๋กœ๋•์…˜์„ ๊ฐ€์žฅํ•˜์ง€ ์•Š์€ ํ…Œ์ŠคํŠธ ์˜ˆ์ œ์ด์ง€๋งŒ ๊ทธ ์•ˆ์—์„œ ๋ช‡ ๊ฐ€์ง€ ์ž‘์—…์ด ์ˆ˜ํ–‰๋˜๋ฉฐ ์œ„์— ๊ธฐ๋ก๋œ ํŒŒ์ผ์„ ๊ตฌ๋ฌธ ๋ถ„์„ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
์†Œ์Šค ์ฝ”๋“œ(ํŒŒ์ด์ฌ)

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

์ฝ”๋ฉ˜ํŠธ๋ฅผ ์ถ”๊ฐ€