Радиоприёмник из DVB стика за $8 — изучаем SDR с GNURadio
Каждый день мы пользуемся множеством радио устройств. Однако редко понимаем, как они работают. Эпоха радиолюбителей фактически прошла, оставив в прошлом любителей спаять приёмник ДВ или СВ своими руками. Да и в методах кодирования техника ушла далеко вперёд. Мы часто слышим рассуждения о взломе умных домов на радио протоколах, о ненадёжности радио связи и т.д. Но многие ли из вас пробовали, например, подслушать Z-Wave сеть умного дома соседа и тем более поуправлять ей? Насколько велика эта угроза для вас?
К счастью в наши дни стали доступны очень удобные средства для работы с радио, а именно SDR.
SDR (Software Defined Radio) позволяет программно перестраивать приёмник и передатчик для работы на различных частотах от 20 до 2000 МГц, после чего произвести обработку сигнала на компьютере с помощью цифровых методов. Это существенно отличает SDR от аналоговых схем радиопрёмников и передатчиков, позволяя легко менять алгоритм обработки полученного сигнала.
Существует множество разных программ для обработки радио сигналов. Я изучил наиболее популярную из них GNURadio. Этот пакет позволяет строить процесс обработки из различных блоков, стыкуя их друг с другом в формате потоковой обработки (pipe). Каждый следующий блок принимает данный от одного или нескольких предыдущих, а вывод передаётся другим блокам.
Под катом я расскажу об основах SDR и GNURadio и о том, как за 8 баксов сделать приёмник AM/FM, китайской управляемой розетки, телеметрии со спутника и всего, что вздумается.
Зачем нам SDR?
Бытовое радио позволяет вам слушать множество станций в различных диапазонах. Однако в эфире есть ещё много всего другого! И вам не нужно никуда подключаться для получения этих данных — эти данные ежесекундно пролетают в эфире мимо вас. Можно сказать, каждый день нас пронзают гигабайты интересных данных. Достаточно их принять и детектировать. Не любопытно ли? Лично мне очень.
Используя SDR передатчик можно ещё и самим создавать радио передатчики. Например, сделать радионяню. Или управлять умными бытовыми устройствами на 433 МГц или даже Z-Wave.
Однако хочется сразу предостеречь: изучите законодательство и решения ГКРЧ на используемые вами диапазоны частот и не нарушайте их!
Лично я пришёл к SDR, уже много лет занимаясь разработкой устройств Z-Wave. Как я уже писал, разработчикам не дают доступ к радио приёмнику-передатчику. Потому мне всегда было интересно, а смогу ли я перехватить сигнал соседской сети Z-Wave или поиздеваться над соседом, включая его приборы (ещё не все устройства Z-Wave имеют шифрование, потому это пока возможно). Ведь настолько же уязвима и моя Z-Wave сеть. Оказалось, это не так просто (пока не смог сделать этого с GNURadio, не используя готовые средства), но надеюсь, я к этому приду и даже напишу в одной из будущих статей. А пока (в следующей статье) будем довольствоваться приёмом команд от пульта к розетке из китайского набора на 433 МГц, а также отправкой команд включения/выключения. Тут протокол оказался в разы проще.
Но вернёмся к азам.
Матчасть
Начнём с небольшого экскурса в теорию. Я предполагаю, что читатель помнит тригонометрию и может самостоятельно погуглить про преобразование Фурье или почитать ссылки, которые я привожу.
Для передачи данных по радио используют модуляцию. Модуляция — это способ преобразовать низкочастотный сигнал в высокочастотный, чтобы потом, после передачи, его демодулировать, т.е. преобразовать обратно в низкочастотный. Высокую частоту при этом называют несущей, т.к. именно эта частота (а точнее диапазон частот вокруг несущей частоты) и будет переносить данные при передаче.
Например, если мы передаём команды между Z-Wave устройствами, несущая частота будет 869 МГц.
Понятно, что оцифровывать сигнал на такой частоте даже современным компьютерам не под силу — для надёжной оцифровки потребуется запускать АЦП примерно каждые 8000 раз в секунду (если мы считаем 10 отсчётов на период достаточным шагом по времени). Однако в этом и нет смысла, ведь интересует нас совсем небольшая полоса частот 869 МГц ± 200 кГц, т.е. менее 0.5 МГц шириной.
Вспомним, что при умножении на , результат можно представить в виде суммы двух синусоид с частотами и . Если выбрана близко к несущей, то отбросив последнее слагаемое (применив фильтр низких частот — ведь последнее слагаемое содержит удвоенную частоту несущей), мы получаем возможность исследовать низкочастотный сигнал от -200 кГц до +200 кГц, что уже под силу даже микроконтроллеру (например, чипам Z-Wave SD3502 или различным чипам TI или SiLabs). Те, кто помнят устройство старых радиоприёмников, увидят здесь аналогию с гетеродином.
Работа SDR состоит в том, чтобы сгенерировать синтетический сигнал на нужной частоте, перемножить его с отфильтрованным входным сигналом с антенны, пропустить через фильтр низких частота, убрав высокочастотную компоненту, и подать на АЦП. Аналогично происходит передача — после ЦАП сигнал умножается с высокочастотным сигналом и излучается антенной.
Блок-схема SDR приёмника показывает, как преобразуется входной сигнал с антенны.
После предварительной фильтрации и умножения входного сигнала на синтетический сигнал (и на сдвинутый на ¼ фазы, что соответствует переходу от к ), оба получившихся сигнала усиливаются, проходят фильтр низких частот (чтобы убрать наложение высокочастотных составляющих при дискретизации) и оцифровываются. Оцифровка внутри SDR приёмника обычно проводится на скорости 50 МГц (обычно пишут про 50 MSPS, Million Samples Per Second, миллионов отсчётов в секунду), после чего применяется ещё один фильтр, преобразующий поток в нужное количество отсчётов в секунду (обычно не более 2–5 MSPS). Этот последний процесс называется децимацией. Полученные сигналы называют I и Q соответственно.
Во-первых, такое возможно при желании проредить в целом соотношении, например, 1:2 или 1:3. Но так не получится при желании проредить в 1.5 раза.
Во-вторых, представим, что наш сигнал имеет амплитуды 0 5 10 0 5 10 0 5 10. При прореживании в 3 раза мы отбросим каждое второе и третье значения, получив последовательность 0 0 0. Очевидно, это не соответствует действительности! Мы должны были получить 5 5 5.
Поэтому децимация проводится путём усреднения по нескольким последним значениям. Если требуется прореживание с дробным соотношением, то предварительно проводится интерполяция.
Эти две величины I и Q дают возможность перейти от вещественного сигнала к комплексному представлению , где в общем случае комплексное число, мнимая часть которого говорит о фазе, а действительная об амплитуде. В таком виде математика выглядит значительно проще. Например, указанный сдвиг частоты в таком представлении производится путём простого умножения на .
Важно помнить, что физически значимый сигнал — это действительная часть Re нашего комплексного представления.
Железо
Специализированная радиоаппаратура стоит дорого. Однако нам повезло — большинство современных приёмников и передатчиков являются именно SDR, т.е. программно настраиваются на нужные частоты. И некоторые из доступных дешёвых приёмников даже дают доступ к данным с АЦП. Наиболее популярными примерами таких SDR приёмников являются DVB-тюнеры. Наиболее популярными и удобными являются RTL-SDR, основанные на чипах Realtek RTL2832U. На AliExpress их представлено различное множество при средней цене $8 (ищите по словам RTL2832U и R820T или E4000).
Вот например два донгла из всей подборки, что я купил для тестов.
Также стоит отметить более профессиональные, но всё ещё доступные железки для игр с SDR: HackRF One и Rad1o badge, сделанный на CCC 2015. Данные железки позволяют не только принимать, но и излучать в широком диапазоне частот.
GNURadio Companion
Итак, получив последовательность значений I и Q мы можем приступить к цифровой обработке сигнала.
Пакет GNURadio включает в себя не только множество программных блоков для обработки сигналов, но и графический дизайнер, позволяющий рисовать блок-схему обработки сигнала, преобразуемую при запуске в готовый программный код обработчика. Это очень удобный способ обработки данных, т.к. он позволяет следить исключительно за смыслом обработки и не обращать внимание на детали программного кода.
Какие же действия позволяют делать блоки в GNURadio? Это базовые математические операции над сигналом (сложение/вычитание сигналов, умножение), а также более сложные, например та же децимация, интерполяция, и различные фильтры.
Большинство фильтров работают не с самим сигналом, а с его спектром. Например, фильтр низких частот работает по принципу преобразования спектра сигнала, уменьшая высокие частоты, после чего полученный спектр обратно преобразуется в сигнал. Вообще, спектр играет очень важную роль в обработке цифровых сигналов.
Дискретизация и спектр сигнала (опять матчасть)
Пожалуй самыми важными характеристиками при построении блок-схемы обработки сигнала являются sample rate (сколько отсчётов в секунду мы получаем), измеряемая в MSPS и спектр сигнала. Они сильно связаны друг с другом.
Как известно из математики, любую периодическую функцию можно представить в виде преобразования Фурье, а любую периодическую функцию можно разложить ряд Фурье с бесконечным количеством слагаемых. Если же мы говорим о последовательности дискретных значений, т.е. нам важны значения только в определённые моменты времени, то ряд Фурье будет состоять из конечного количества слагаемых, равного количеству исходных значений:
Такое разложение называется Дискретным Преобразованием Фурье или ДПФ.
Видно, что наивысшая циклическая частота, которая участвует в спектре сигнала, составляет . Ведь sample rate — это количество значений в секунду (N штук), т.е. шаг по времени составляет секунд, а наивысшая частота Гц. Например, если sample rate составляет 2 000 000, то максимальная частота будет 2 МГц. Теперь становится понятно, что для анализа полосы от -200 до 200 кГц нам нужно иметь sample rate не менее 400 000.
Есть ещё одно интересное и неочевидное свойство ДПФ: спектр дискретной функции периодичен, т.е. . Математически это легко доказывается. Почему именно это нам важно? При изучении спектра в окне, отображающим спектр в GNURadio, вы будете часто задумываться о том, что там справа и слева (ведь там должны быть видны спектры соседних радиостанций, подсказывает нам интуиция). Однако это не так, вне окна спектр повторяется точно так же ещё раз, и ещё раз. Но как же соседние частоты и радиостанции? Куда они делись? Информация о них потерялась в тот момент, когда мы с определённой частотой дискретизации (sample rate) оцифровали сигнал вокруг несущей.
Теперь, надеюсь, стало понятно, из каких соображений необходимо выбирать sample rate: спектр изучаемого сигнала должен уместиться в спектральную ширину окна, соответствую выбранному sample rate. У большинства SDR sample rate не может превышать 2–5 MSPS.
Весьма хорошо о преобразовании Фурье, ДПФ и БПФ (Быстром Преобразовании Фурье) написано тут.
Итак, мы разобрались, что для вычисления спектра сигнала при помощи ДПФ, значения собираются в последовательность N штук. Каждое следующее значение добавляется, вытесняя самое старое (FIFO).
Практика. FM приёмника на GNURadio
Итак, у меня есть китайский стик на базе RTL2832U и R820T2. Первое, что мы попробуем сделать — это сделать FМ приёмник. Частотная модуляция не самая простая и я сначала хотел всё продемонстрировать на примере амплитудной модуляции. Но увы в диапазоне > 20 МГц нет станций с AM модуляцией.
Откройте GNURadio Companion и в блоке Options сразу выберем WX GUI (самостоятельно изучите интерфейс, предоставляемый QT). Для этого дважды кликните на блок и измените свойство.
Добавьте блок osmocom source и сразу измените значение Ch0: Frequency на freq. GNURadio написана на Python, и в любые числовые поля можно подставлять выражения на питоне. Внимательно следите за типом полей — если это float, то результатом должно быть действительное число, если int, то целое. Обратите внимание, что sample rate уже связана с переменной samp_rate, определённой в отдельном блоке. Измените samp_rate на 2e6, т.е. мы зададим полосу нашего сигнала 2 МГц.
Переменная freq будет задаваться слайдером. Для этого добавьте блок WX GUI Slider, смените ему ID на freq и установите Default, Minimum и Maximum на 100e6, 50e6 и 150e6, соответственно (заметим, что запись 100e6 является валидным числом nbgf float в Python). При движении слайдера значение freq будет меняться от 50 000 000 до 150 000 000 с шагом в 1 000 000 (100 делений, смотри поле Num Steps).
Теперь добавим блок WX GUI FTT Sink (графическое отображение результатов ДПФ). Соедините вентиль in этого блока с вентилем out блока osmocom source. Этим мы указали GNURadio, что вывод одного блока необходимо передать на вход другого блока.
Запустим наш проект. В появившемся окне можно видеть спектр с частотами от -1 МГц до 1 МГц (как мы и заказывали, диапазон 2e6 МГц в соответствии с samp_rate). Но ведь мы смотрим на диапазон 100 МГц, а не -1 — +1 МГц! Всё дело в том, что в SDR уже произошло умножение частот, и поступающий с SDR сигнал действительно уже не имеет информации об исходной частоте. Поэтому для удобства в блоке WX GUI FTT Sink в поле Baseband Frequency укажите тоже значение переменной freq. После перезапуска шкала частот будет выглядеть наглядней.
Итак, подвигайте слайдер в диапазоне 60–110 МГц и понаблюдайте за спектром: вы видите множество FM станций. Также посмотрите, как сильно меняется приём при приближении вашей руки к антенне. Штатная антенна рассчитана на диапазон примерно 800 МГц, и ваше тело изменяет эффективную длину антенны, делая приём лучше.
А теперь предлагаю послушать вашу любимую станцию. FM демодуляция не очень проста в математике, поэтому в GNURadio даже есть готовый блок FM Demod. Ему нужно указать ширину канала и децимацию для выходного аудио сигнала. Последняя должна равняться отношению текущего sample rate к тому, который должен быть у аудио сигнала (48 кГц). Подключите выход этого блока к Audio Sink. Для удобства заведём ещё одну переменную audio_samp_rate = 48000 (это int!) для битрейта звуковой карты, а децимацию int (samp_rate/audio_samp_rate). Но если такой проект запустить и настроить частоту на одну из станций в вашем городе, то вещание станции будет сильно забито шумами. И это понятно, мы забыли выделить одну станцию из всего диапазона.
Добавим между блоками osmocom source и FM Demod ещё один блок Low Pass Filter. Это фильтр низких частот. Настроим его, чтобы он обрезал всё за пределами 100 кГц с переходом шириной 100 кГц (можно потом подобрать параметры получше). Теперь звук будет намного чище. Также если громкости мала, то можно перед Audio Sink добавить Multiply Const для увеличения амплитуды выходного аудио сигнала.
Несколько полезных советов
От одного блока можно передавать данные не только в один, но и в несколько блоков.
При проектировании блок-схем в GNURadio часто удобно добавлять окно FFT Sink и другие средства визуализации. Однако их большое количество изрядно нагружает процессор. Можно, конечно, удалять такие блоки, когда они уже не нужны. Но в GNURadio есть удобная возможность отключить блок, не удаляя его. Это можно сделать из контекстного меню или нажав быстрые клавиши D[isable] и E[nable].
При анализе сигнала от электронных устройств часто нужно нажимать на них на кнопки для получения радио последовательности. Но можно единожды записать последовательность в файл, после чего проигрывать его и отрабатывать блок-схему. Для записи используйте блок File Sink, а для проигрывания File Source. Тут есть подводный камень: при проигрывании из файла GNURadio не знает ничего о скорости, с которой нужно «подавать» данные в остальные блок, и будет пытаться работать на максимальной скорости, загружая процессор. Для ограничения скорости чтения нам нужно добавить специальный блок Throttle, указав желаемый sample rate.
Если нужно сдвинуть спектр вправо или влево на какую-то величину (например, для применения потом Low Pass фильтра), можно умножить сигнал на синусоиду. Для этого воспользуйтесь блоком Signal Source и перемножьте их при помощи Multiply. Ровно так и делается в SDR приёмнике. Например, можно отправить сигнал в две последовательности блоков. В одной сдвинуть на одну боковую частоту, во второй на другую боковую частоту. Далее в оба отфильтровать фильтром низких частот и сравнивать два сигнала. Именно так делается при детектировании частотной манипуляции.
Кстати, если сделать слайдером изменение частоты Signal Source, то вы на практике увидите, как сдвигаемый за правый край спектр сигнала появляется в противоположной стороне, и наоборот.
При изучении спектров сигналов с некоторыми RTL-SDR вы столкнётесь с постоянным пиком ровно в середине спектра. Причём этот пик не двигается при перестройке частоты, всегда оставаясь посередине. Это явление называется DC spike. Очевидно, эта частота (0 Гц) соответствует константе. Связана она с тем, что в приёмнике перед АЦП бывает присутствует небольшая постоянная составляющая. В GNURadio у блоков osmocom source и RTL-SDR source есть галочка DC Offset Mode, позволяющая убрать эту составляющую.
Помимо записи в файл GNURadio имеет блоки для передачи в UDP/TCP сокеты и получения данных из них. Например, можно даже сделать радио → UDP broadcast вещание.
Хочется попробовать передачу данных, но пока не обзавелись Hack RF One или аналогом? Не беда. Можно потренироваться использовать Audio Sink с Audio Source. Например, можно научиться передавать данные через ультразвуковые волны, используя микрофон и колонки. Вот пример такого проекта: www.anfractuosity.com/projects/ultrasound-via-a-laptop
Что ещё почитать?
gnuradio.org/redmine/projects/gnuradio/wiki
greatscottgadgets.com/hackrf
habrahabr.ru/post/204310