ADS-B (Automatic Dependent Surveillance — Broadcast) — это технология, которая позволяет самолетам автоматически передавать данные о своем местоположении, высоте, скорости и другую информацию. Эти данные передаются в виде радиосигнала и могут приниматься специальными приемниками. В этой статье я расскажу о способах приема ADS-B самолета. Мы рассмотрим простой и сложный способ.
Еще по теме: Обзор лучших SDR для хакера
Способы приема ADS-B самолета
Для точного определения координат обычно используются два числа — широта и долгота. 32 бита float обеспечивают точность до семи знаков после запятой, что в пересчете на координаты дает точность порядка нескольких сантиметров, а если чуть уменьшить точность (до десятков сантиметров), то два таких числа как раз можно запихнуть в 56 бит сообщения, и не придется городить неведомо что с компактными координатами. Точность даже в десятки сантиметров для самолета, летящего со скоростью больше 100 м/с, просто огромна, так что, чем руководствовались авторы протокола, понять сложно.
Тем не менее для записи координат используется формат CPR (Compact Position Reporting), который, как ясно из названия, предназначен для компактной передачи координат. Часть данных мы уже видели в примере сигнала с координатами выше. Поскольку ужать большое количество данных в маленький объем невозможно, эти данные просто разделили на части и отправляют в два захода, а пакеты стали называться просто «четный» и «нечетный». Как из этого получить нормальные координаты? Сейчас покажу!
Представим, что все самолеты летают в 2D-пространстве. Это одно пространство разделим на две сетки с разными параметрами и назовем их четной сеткой и нечетной. Четную сделаем 4 на 4, а нечетную — 5 на 5.
Допустим, мы хотим передать местоположение самолета в сетке размером 16 на 16 — пусть это будут координаты (9,7). Если бы у нас была одна сетка, то мы бы передали просто 9 и 7, а операторы бы у себя на карте нас нашли, но в CPR на самом деле сеток две.
В таких сетках мы укажем наше положение (9,7) в виде (1,3) на четной сетке (слева) и (4,2) в нечетной сетке (справа). Когда оператор получает оба сообщения, ему нужно совместить их на обеих сетках.
Если наложить сетки с полученным координатами друг на друга, в точке совпадения и будет находиться искомый объект.
Я описал алгоритм без математических выкладок и прочих сложностей, чтобы ты мог примерно представить себе, как работает восстановление координат из двух частей. Реальная сетка далека от случая из примера и выглядит, как на картинке ниже.
Простой способ принять ADS-B
Теперь, когда мы знаем устройство всех основных частей протокола, можно попробовать принять реальный сигнал. Для приема любого подобного сигнала потребуются три базовые вещи: антенна, приемник и ПК.
Антенна
Начнем с самого важного предмета, без которого не обходится никакая радиосвязь, — антенны. Выбор антенны зависит от множества факторов, включая частоту, направленность сигнала, среду его прохождения. Наш сигнал передается на частоте 1090 МГц, и принимать мы его будем на открытом воздухе. Самый простой вариант (но не самый эффективный) — это обычная штыревая антенна. Такую антенну можно сделать из куска провода или проволоки, главное здесь — правильно рассчитать ее длину. Длина антенны зависит от длины волны, которую мы хотим на эту антенну принимать. Длина волны — это расстояние между двумя соседними «горбами» сигнала (на рисунке снизу).
Лямбда — это и есть длина волны, а получить ее из частоты можно по формуле λ = C/f, где С — скорость света, а f — частота сигнала. Для 1090 МГц она будет примерно равна 27,5 см. Если взять металлический штырь такой длины, то у тебя получится так называемая полноволновая антенна, которую можно спокойно укоротить вдвое или даже в четыре раза, чтобы получить полуволновую или четвертьволновую антенну соответственно. Разница в чувствительности этих антенн, конечно, будет, так что советую использовать полуволновую антенну, длина которой составит примерно 13,75 см.
Если хотите поколхозить по полной, то в сети можно найти гайды, как сделать дипольную антенну для работы с ADS-B из пробки и проволоки.
Делать свою антенну я не буду — это не самое простое занятие, да и нужная антенна у меня уже была. Тебе могут подойти, например, антенны для раций, если принимать на открытом пространстве и вокруг не слишком много шумов. Я использую обычную штыревую coil-loaded-антенну, которая, по сути, работает как штыревая, но за счет катушки имеет меньшую длину.
Основные характеристики антенны можно измерить специальным векторным анализатором, который генерирует разные частоты и проверяет реакцию антенны на них.
Вывод анализатора кажется сложным на первый взгляд, но на самом деле все довольно просто. Чтобы узнать, годится ли антенна для какой‑то конкретной частоты, достаточно посмотреть на желтую линию SWR, которая по‑русски называется КСВ (коэффициент стоячей волны). Этот коэффициент показывает, какую часть сигнала антенна передала в эфир, а какая вернулась. Чем меньше сигнала вернулось, тем лучше антенна работает на конкретной частоте.
На приборе видно, что на метке 1 (я выставил ее на частоту 1090 МГц) SWR равен 1,73, что очень даже неплохо. Обычно хорошей антенной считается та, у которой КСВ около 1 (не больше 2).
Приемник
В качестве приемника мы будем использовать SDR-донгл. Он представляет собой обыкновенное радио, которое управляется с помощью специальных программ, а не крутилкой с делениями, как на дедовских приемниках. Для приема ADS-B подойдет любой SDR-адаптер, начиная с самого дешевого RTL-SDR и заканчивая дорогими BladeRF и ему подобными. Цена дешевых вариантов начинается от 30 долларов, так что приобщиться может каждый.
Я буду использовать BladeRF micro — он поддерживает большой диапазон частот и имеет большую частоту выборки, о которой поговорим позже.
Если у вас еще нет SDR — не беда, RTL-SDR стоит всего около 30 долларов и продается во многих интернет‑магазинах. Вот только имейте в виду, что этот прибор умеет только принимать сигналы, но не умеет передавать их.
Собираем всё вместе
Теперь, когда у тебя есть антенна и SDR, нужно найти свободное от помех и препятствий место — я просто выехал за город километров на десять. Сигналы около 1 ГГц (к которым относится и наш ADS-B) почти не распространяются за горизонт, так что, если ты не живешь около аэропорта и вокруг есть препятствия, можно не поймать вообще ничего.
Как улучшить прием
Между антенной и SDR можно добавить фильтр, который отсечет часть шумов. Существуют специальные фильтры для ADS-B, заказать из Китая можно за 10–15 долларов.
Для изучения эфира я буду использовать GQRX, эта программа доступна для Linux и macOS. Если у тебя Windows, рекомендую SDR#. В Ubuntu GQRX устанавливается из штатных репозиториев:
1 2 |
apt update apt install -y gqrx |
Дальше делаем звук погромче, выбираем источником наш SDR и нажимаем на большую кнопку «Старт». Если все сделано правильно, из динамиков начнет шипеть так, что ты подпрыгнешь на стуле, после чего отключишь звуки кнопкой Mute, расположенной в правом нижнем углу.
Частоту приема можно выбрать в верхней части экрана — нужно задать там значение 1.090.000, что равно 1090 МГц. После этого ты увидишь что‑то похожее на скриншот внизу.
Короткие полоски по центру — это и есть сигнал ADS-B, который заметно выделяется на фоне окружающего шума. Если у тебя их нет, попробуй изменить параметры усиления на вкладке Input Controls справа. Если и это не помогло, перейди на вкладку FFT Settings и настрой параметры Plot и WF. Еще можно попробовать покрутить антенну или положить ее в разных направлениях.
Dump1090
Когда ты добьешься стабильного приема сигнала в GQRX, можешь переходить к следующему шагу.
В обычных случаях, если кто‑то хочет просто принять сигналы Mode S и декодировать их, он использует уже готовую программу dump1090. Эта тулза с исходным кодом умеет демодулировать и декодировать почти все сигналы Mode S и даже выводит их в виде красивой таблички. Чтобы проверить, все ли у нас работает корректно, лучше начать с чего‑то заведомо рабочего, и начнем мы с dump1090.
Для установки нужно клонировать репозиторий проекта с GitHub и собрать бинарник. Делается это проще простого:
1 2 3 |
git clone https://github.com/antirez/dump1090 cd dump1090 make |
После этого у тебя должен появиться бинарник dump1090. Если у тебя RTL-SDR, можешь использовать dump1090 напрямую с ним, но у меня BladeRF, для поддержки которого нужно немного повозиться.
Во‑первых, установи драйвер для своего SDR — их можно найти в репозиториях практически всех дистрибутивов, погуглить.
Во‑вторых, нужно будет накатить на SDR специальную прошивку. Для BladeRF эти прошивки доступны на сайте Nuand, где нужно выбрать соответствующий файл для своей версии BladeRF.
Дальше скачиваем программу для декодирования сообщений ADS-B и собираем ее:
1 2 3 |
git clone https://github.com/Nuand/bladeRF-adsb cd bladeRF-adsb/bladeRF_adsb make |
Теперь заливаем прошивку в BladeRF. Сделать это можно с помощью пакета bladerf-cli:
bladeRF-cli -l ~/Downloads/adsbxA4.rbf
Теперь запускаем в одном окне dump1090, а в другом — bladeRF-adsb, который мы собирали парой шагов раньше:
1 2 |
~/Soft/dump1090/dump1090 --raw --device-type bladerf --bladerf-fpga '' ~/Soft/Blade/bladeRF-adsb |
Если все сделано верно, в окне с dump1090 ты увидишь кучу строк в шестнадцатеричном виде. Это и есть сообщения Mode S, которые надо еще декодировать и отфильтровать.
Если убрать —raw из аргументов запуска dump1090, то сигналы будут автоматически декодироваться и выводиться в таблицу, как на скриншоте ниже.
В таблице можно найти уже декодированные из CPR широту и долготу, позывной самолета, его скорость, высоту и другие данные. Но это было бы слишком просто, не правда ли? Давай декодировать эти данные самостоятельно!
Сложный способ принять ADS-B
Чтобы принять сигнал самостоятельно, нужно сначала демодулировать его, а потом декодировать то, что получится.
Демодуляция (детектирование сигнала) — процесс, обратный модуляции колебаний, выделение информационного (модулирующего) сигнала из модулированного колебания высокой (несущей) частоты. Простым языком демодуляция — это выделение сигнала из колебаний высокой частоты.
ADS-B работает с PPM-модуляцией, то есть, чтобы выделить полезный сигнал, нужно каждые 0,5 мкс определять состояние сигнала (0 или 1). План у нас будет следующий:
- Записать сигнал.
- Сделать выборки каждые 0,5 мкс.
- Преобразовать их в байты.
- Декодировать сообщения ADS-B.
Для удобства будем использовать Python либо готовый набор инструментов GNU Radio. На Ubuntu GNU Radio устанавливается следующим образом:
1 2 3 |
sudo add-apt-repository ppa:gnuradio/gnuradio-releases -y sudo apt update sudo apt install gnuradio -y |
Разумеется, для работы GNU Radio потребуются и драйверы для твоей SDR. При этом мы не будем использовать SDR как основной источник сигнала, чтобы можно было на заведомо одинаковых данных погонять разные алгоритмы и сравнить результаты.
Запись сигнала
Первое, что нужно сделать после запуска GNU Radio, — это собрать базовый пайплайн. Выглядеть он будет, как на скриншоте ниже.
Всего тут пять блоков, два из которых — опции и переменная. В самом начале стоит osmocom Source, который отвечает за получение сырых данных из SDR. Настроек в нем много, но для работы нужны всего пять (пример ниже — для BladeRF):
- Device Argument — аргументы, которые будут переданы SDR. Тут вписываем bladerf=0.
- Ch0 Freq — частота нулевого канала. У BaldeRF их два, но нам нужен только один. Сюда вписываем частоту ADS-B — это 1090 МГц или 1,09 ГГц.
- RF Gain — параметр, задающий чувствительность приемника. Я установил его в 15 дБ. Не ставь слишком большую чувствительность — мощные сигналы могут повредить радиотракт!
- Bandwidth — ширина канала приема, для ADS-B это 2 МГц.
Пятый параметр — Sample Rate, или частота дискретизации. Простым языком это количество выборок в секунду, которое делает SDR для оцифровки сигнала. Что это и зачем? На вход SDR поступает аналоговый сигнал, а для работы его нужно оцифровать. Для оцифровки сигнала нужно делать выборки через некоторый одинаковый промежуток времени. Чем больше выборок в секунду, тем лучше, но тем мощнее для этого должно быть устройство. Наглядно процесс оцифровки показан на картинке ниже.
Количество столбиков на картинке — это и есть количество семплов, а SPS в скобках в названии этого параметра расшифровывается как Samples per Second, то есть количество выборок в секунду. В параметре samp_rate вверху я задал частоту выборки 4М SPS, и это неслучайное значение, ведь оно как раз в четыре раза больше частоты входного сигнала, а по теореме Найквиста нужна минимум в два раза большая частота, чем частота сигнала. Для работы с ADS-B можно использовать и частоты 2 и 2,5 MSPS, но чем выше частота дискретизации, тем лучше качество оцифровки.
Теорема Найквиста (она же теорема отсчетов, теорема Котельникова) — фундаментальное утверждение в области цифровой обработки сигналов, связывающее непрерывные и дискретные сигналы и гласящее, что «любую функцию F(t), состоящую из частот от 0 до f1, можно непрерывно передавать с любой точностью при помощи чисел, следующих друг за другом менее чем через 1 / (2 * f1) с».
Теперь, когда все настроено, можно нажимать «Старт» на верхней панели GNU Radio. Если все сделано верно, ты увидишь спектр, похожий на тот, что поймал я.
Еле заметные полоски в верхней части скриншота — это и есть сигналы, которые мы ищем. Мой BladeRF не откалиброван, так что по бокам видны завалы, но нашему эксперименту они не мешают. Нужно записать несколько минут таких сигналов, после чего можно сворачивать оборудование и идти домой — декодировать!
Демодуляция
Для демодуляции я собрал в GNU Radio более сложную и интересную схему.
Давай пробежимся по каждому блоку схемы, чтобы ты понимал, что происходит. На схеме часть входов и выходов имеют разные цвета — эти цвета означают разные типы данных:
- синий — комплексные числа;
- оранжевый — числа с плавающей точкой;
- фиолетовые — байты.
Теперь перейдем к самим блокам.
- File Source — источник сигнала в виде файла. В этом блоке нужно выбрать файл, который мы записали, когда принимали сигнал. Этот блок заменяет реальную SDR для наших экспериментов.
- Throttle — элемент, замедляющий подачу сигнала в нашу схему. Без этого блока вся обработка выполнится слишком быстро и будет ничего не видно.
- Waterfall — это визуализация сигнала. Желто‑голубая каша из пикселей на графике — это как раз оно.
- QT GUI Time Sink рисует график зависимости сигнала от времени.
- Compex To Mag — блок преобразования комплексных чисел в числа с плавающей точкой. На входе у него синие значения, а на выходе оранжевые.
- Threshold — самый важный блок в нашей схеме демодуляции, он помогает отделить сигналы от остального шума. Я задал значения low 13m и high 13m — это амплитуды сигналов, которые я хочу отделить. Чтобы найти порог для твоей SDR, придется собрать еще одну дополнительную схему. Справа в поиске найти блоки Peak Detector и Time Sink. В блоке Time Sink нужно выставить три входа, а если у Peak Detector отсутствует Debug port, то только два. После сборки схемы ты увидишь, на какой амплитуде будут пики полезного сигнала.
- Symbol Sync — это еще один важный блок синхронизации символов. Выше я уже говорил, что выбрал частоту дискретизации 4 MSPS, и нужно это как раз для работы блока синхронизации, поскольку его минимальный параметр Samples per Symbol равен 2, а возможно это, только если частота дискретизации входного сигнала не меньше 4 MSPS. Если у тебя не такой крутой приемник (например, RTL-SDR) — не беда, можно отключить Symbol Sync и использовать блок Keep 1 in N (нарисован серым на схеме выше). С блоком синхронизации из сырой записи я в своем тесте получил 18 из 31 хороших пакетов, а без него — 11 из 29. Получается, что, просто используя нормальную синхронизацию, ты получишь значительно больше валидных данных.
- Float to Char — этот блок преобразует числа с плавающей запятой в символы и отправляет в File Sink.
- File Sink сохраняет байты в файл для дальнейшего анализа.
Со схемой разобрались, можно ее включать. После запуска ты увидишь примерно такую картину.
Самое первое — это наш Waterfall, на нем удобно глазами искать сигнал. Дальше идет Time Sink после синхронизации: тут два сигнала — красный (сигнал ошибки) и синий (исправленный по времени сигнал). Эти сигналы уже напоминают что‑то цифровое, но настоящий цифровой сигнал отображается уже на третьем графике (обработанный блоком Threshold). Четвертый график — это тот же исходный сигнал, что на первом графике, но обработанный блоком Threshold.
Анализ файла
После демодуляции файл будет выглядеть так.
1 2 3 4 5 6 7 8 9 10 |
000717e0: 0000 0100 0100 0000 0000 0001 0000 0000 ................ 000717f0: 0101 0000 0100 0000 0000 0101 0000 0100 ................ 00071800: 0101 0001 0000 0100 0101 0000 0101 0001 ................ 00071810: 0000 0101 0001 0001 0100 0101 0000 0100 ................ 00071820: 0100 0100 0100 0101 0001 0001 0001 0000 ................ 00071830: 0100 0100 0100 0100 0101 0000 0100 0100 ................ 00071840: 0100 0001 0101 0000 0100 0100 0100 0100 ................ 00071850: 0100 0100 0100 0100 0100 0100 0100 0100 ................ 00071860: 0100 0100 0100 0100 0100 0100 0100 0100 ................ 00071870: 0100 0100 0100 0100 0100 0100 0100 0101 ................ |
Давай теперь прочитаем файл и будем искать в этом потоке данных преамбулу пакета Mode S. Я написал для этого несколько функций на Python.
1 2 3 4 5 6 7 8 9 10 11 |
import requests def read_binary_file(filename): with open(filename, 'rb') as file: return [int(byte) for byte in file.read()] def find_pattern(data, pattern): pattern_length = len(pattern) matches = [] for i in range(len(data) - pattern_length + 1): if data[i:i+pattern_length] == pattern: matches.append(i) return matches |
Первая функция просто читает файл, а вторая ищет заданный паттерн (нашу преамбулу). Но для поиска нужно сначала собрать двойные биты в одинарные (те самые повторения, из‑за которых бит по 0,5 мкс занимает 1 мкс). Следующая функция как раз это и делает:
1 2 3 4 5 6 7 8 |
def decode_adsb(bits): decoded = [] for i in range(0, len(bits), 2): if bits[i:i+2] == [1, 0]: decoded.append(1) elif bits[i:i+2] == [0, 1]: decoded.append(0) return decoded |
Читаем файл и находим все преамбулы:
1 2 3 4 5 6 |
data = read_binary_file("decoded2.bin") pattern = [1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0] matches = find_pattern(data, pattern) print("Total matches: ", len(matches)) good = 0 bad = 0 |
Здесь pattern — это и есть искомая преамбула. Можешь даже отлистать в начало статьи и сравнить. Поскольку преамбула означает любой сигнал Mode S, а не только ADS-B, то могут попадаться сигналы длиной 56 бит. Их поддержка тоже есть.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
for match_index in matches: try: start_index = match_index + len(pattern) end_index = start_index + 224 end_index2 = start_index + 112 extracted_bits = data[start_index:end_index] extracted_bits2 = data[start_index:end_index2] decoded_bits = decode_adsb(extracted_bits) decoded_bits2 = decode_adsb(extracted_bits2) hex_value = hex(int(''.join(map(str, decoded_bits)), 2)).replace('0x','') hex_value2 = hex(int(''.join(map(str, decoded_bits2)), 2)).replace('0x','') if len(hex_value) == 28: response = requests.get(f"http://jasonplayne.com:8080/decode?packet={hex_value}&refLat=&refLon=") if "Failed to decode." in response.text: bad += 1 else: good += 1 print(response.text) if len(hex_value2) == 14: response = requests.get(f"http://jasonplayne.com:8080/decode?packet={hex_value}&refLat=&refLon=") if "Failed to decode." in response.text: bad += 1 else: good += 1 print(response.text) except: pass print("Total good/bad: ", good, "/", bad) |
Прочитав код, ты заметишь, что я вытаскиваю ADS-B-пакеты и отправляю их для декодирования на сторонний сервис, чтобы не изобретать собственный парсер для всех типов пакетов. Этот же сайт можно открыть и просто в браузере, чтобы полистать образцы пакетов и еще глубже окунуться в мир Mode S и ADS-B.
При хорошем раскладе ты увидишь примерно такой вывод:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
8d89617999086e99b8480ab9e174 MODE S Packet: Length : 56 bits Frame : 8d89617999086e99b8480ab9e174 DF: Downlink Format : (17) ADS-B CA: Plane Mode S Cap: (5) Level 2,3 or 4. can set code 7. is airborne VS: Vertical status : Airborne AA: ICAO : 896179 ME : ADSB Msg Type : (19) Airborne velocity SUB: Sub Type : 1 Intent Change : false IFR Capable : false Nav Accuracy Cat : 1 heading : 151.88 Super Sonic? : No velocity : 232.65 EW/NS VEL : (East/west: 109) (North/South: -204) Vertical Rate : -1088 HAE Delta : 225 (Height Above Ellipsoid) |
Я получил сигнал со скоростью самолета, но у тебя, конечно, будет их намного больше. DF у меня равен 17 (ADS-B), а ICAO-код транспондера — 896179.
Надеюсь, и тебе понравилось погружение в интересный мир радиосигналов. Приятных исследований!