Изучаем GNU Radio при помощи микрофона

bc8b4024c02946b285d2c0c32b11810f.png

Прошлая статья про SDR и GNU Radio показала, что тема интересна сообществу. Учитывая, что про пакет GNU Radio информации на русском языке почти нет, да и на английском не всё понятно, я решил описать свои опыты с GNU Radio.

Про SDR и GNU Radio я уже писал в предыдущей статье. Напомню, моя цель показать, как перехватывать и даже излучать радио сигнал для управления устройствами умного дома (да и вообще IoT). Считаю важным привлечь внимание к безопасности в IoT. Но до этого нам ещё далеко… Для начала разобраться бы с GNU Radio!

SDR-приёмник есть не у всех, и мне показалось, что будет полезным показать, что можно сделать с GNU Radio с тем, что есть у каждого —, а именно с микрофоном вашего ПК и наушниками.

Под катом несколько интересных экзерсисов со звуком.

Звуки вокруг


Итак, начнём с простого: изучения спектра звуков вокруг. Будем считать, что установку пакета GNU Radio или запуск подготовленного образа Ubuntu/Windows с GNU Radio вы произвели. Если нет, то на сайте GNU Radio скачайте образ на базе Ubuntu.

Начнем с создания простого проекта захвата звука с микрофона. Для этого добавим блок Audio Source, выставим samp_rate 48000 (многие карты работатю на 44100 Гц, а не на 48000 Гц).

Узнать предел вашей карты на Linux можно так:
$ pactl list short sinks
0       alsa_output.pci-0000_00_03.0.hdmi-stereo        module-alsa-card.c      s16le 2ch 44100Hz       SUSPENDED
1       alsa_output.pci-0000_00_1b.0.analog-stereo      module-alsa-card.c      s16le 2ch 44100Hz       SUSPENDED


Сразу встаёт вопрос: почему 48 кГц? Ведь, говорят, слышим мы до 20 с мелочью кГц, и то не все.
Если обратиться к теореме Котельникова-Найквиста-Шеннона, то станет понятно, что частота дискретизации должна в 2 раза превышать верхнюю частоту полезного сигнала. Итак, 24 кГц — уже ближе. Неплохо об этом написано тут.

Вообще числа 48 кГц и 44.1 кГц — дань прошлому. Они связаны с количеством кадров в секунду и строками развёртки в древних системах записи видео, потом с форматом CD. В наши дни эти значения так и осталось стандартами.


Вернёмся к нашей схеме GNU Radio. К Audio Source подключим WX FFT. Не забываем QT GUI заменить на WX GUI в top_block, а так же изменить тип входа на Float. А теперь будем бегать по квартире, издавая разные звуки. Школьная физика нам много рассказала о том, что мы увидим, но все это забыто, да и своими глазами увидеть всегда интересней, чем просто прочесть в учебнике.

Первое, что мы увидим — ничего не видно! Вся значимая часть спектра сосредоточена в первых 2 или 3 кГц. Ну, ок, давайте добавим между источником и WX FFT новый блок: Rational Resampler — он позволит изменить Sample Rate, оставив только нужную часть спектра. Для удобства заведём новый блок Variable (переменную), назовём resamp и зададим значение, например, 15. В блок ресемплера в поле Decimation впишем имя этой переменной. Теперь на WX FFT будет подаваться сигнал с Sample Rate равным samp_rate/resamp, т.е. в этом блоке в поле Sample Rate нужно вставить именно это выражение. Теперь наш сигнал будет простираться до 1.5 кГц, что уже лучше. Ну, теперь всё видно!

Итак, начните с ноты ля 440 Гц. Проверим свой голос на частоту чистоту. Сразу интересная находка (напомню, мы это должны знать из школы): мы издаем не одну частоту, а набор кратных частот — тон и обертоны. У кого есть дома пианино или другой инструмент, можно попробовать и их. Вот так нота ля первой октавы выглядит на пианино.

0fd12b9623384b0fa4b2e3d496400d53.png

А вот ля малой октавы. Всё как по учебнику — пиков стало в 2 раза больше, низший пик стал в 2 раза ниже (220 Гц)

61198ee72238459a9544271b6b953815.png

Кратные частоты присутствуют не случайно. Ведь в случае пианино/гитары/… (струнных) звук извлекается колебанием струны. А у струны два закреплённых конца, т.е. могут излучаться только моды с длинной волны λ/2 = L*n, где n=1,2,3…

Аналогично с голосом. Связки позволяют менять характеристики гортани, которая выступает резонатором (да простят меня любители анатомии за неточности названий). Опять же, стенки закреплены, там у колебаний узловые точки, т.е. опять же та же формула для возбуждаемых мод.

А теперь хлопнем:

8ca8a41a653c41fab6720f344bb861be.png
(синее — спектр «тишины», зелёное — спектр хлопка)

Ой, тут прямо все частоты видны (обратите внимание на шкалу частот и уровень сигнала в сравнении с графиками выше!). Логично, ведь хлопок — это кратковременное повышение давления (очень резкое), т.е. почти δ-функция, а её спектр содержит все частоты.

Это кстати можно использовать. Кто помнит из 90-ых такие брелоки для поиска ключей, которые на свист начинали звуки издавать? Удобная штука была. Так вот они на хлопок тоже реагировали, т.к. в звуке хлопка есть в том числе и частота, соответствующая свисту.

Спектр алфавита


А теперь давайте послушаем себя. Произносите разные буквы алфавита и смотрите на спектр (лучше без ресемплинга, чтоб видеть, какие частоты используются в диапазоне 0–20 кГц). Человек использует несколько видов генерации звуков: губные (начинаются с размыкания губ, что создаёт много частот сразу), гортанные, зубные, язычные и носовые. Особенно интересно выглядят шипящие и свистящие (это гортанные и зубные) — их спектр очень широк и в основном располагается в спектре > 2.5 кГц (у звука «с» даже > 5 кГц). Это объясняет, почему слова с этим звуком плохо слышны по телефону (в былые времена полоса пропускания была на уровне 3 кГц или даже ниже — попробуйте добавить Low Pass фильтр и направить результат в Audio Sink с наушниками — результат будет напоминать старый добрый проводной телефон).

У кого есть дети, послушайте их — они сильно звонче, их голос содержит много высоких частот. Именно поэтому их голос по телефону всегда странный, а понять их частенько просто невозможно (искажения голоса сильно больше, чем у взрослого).

Кстати, я попробовал выдавить из себя самую высокую ноту. Ну, выше 700 Гц я не взял. Сын взял 1200 Гц! Я такой ограниченности от себя не ожидал — это же меньше 5% слышимого мной спектра. Почувствовал себя ущербным…

Поговорим с дельфинами


Ну ок, мы не можем так высоко звучать, так воспользуемся этой полосой для другого. Теперь попробуем сделать передачу голоса по ультразвуку. Поставим Low Pass фильтр (низких частот) после блока Audio Source, умножим на косинус 15 кГц (тем самым перенесём наш сигнал на 15 кГц выше) и отправим в динамик (он так себе с этим справляется). Но такой файл можно записать, используя File Sink. Проиграв такой файл, сторонний слушатель разобрать, что там сказано, не сможет. Шпионская игра на уровне 2 класса.

417499daf73c42f2a226655f3670511d.png

А теперь произведём обратное — восстановим закодированный сигнал. Обычный микрофон ноута плоховато воспринимает высокие частоты, а вот из файла — легко (для наших-то тестов достаточно). Голос после восстановления весьма неплохо воспринимается.

ea628aeff2d048f99d1f3f1934afc0eb.png

Примерно таким способом можно даже данные передавать: вот пример сетевого интерфейса на базе ультразвуковой передачи.

Данный пример показывает, что с GNU Radio и звуковой картой можно делать уже немало!

Обзор блоков GNU Radio


В GNU Radio очень много разных блоков, и разобраться в них не просто. Причём самое сложное — понять, что тут вообще есть. Ниже я приведу лишь наиболее популярные блоки, используемые «в быту».Математические операции:
Add — сложение двух сигналов
Mulitly — перемножение двух сигналов (например, для АМ)
Add Const — прибавить (вычесть) константу (например, при демодуляции АМ убрать DC-составляющую)
Multiply Const — умножение сигнала на константу (для усиления, например)
Char/Integer/Float/Complex to Char/Integer/Float/Complex — преобразование типов данныхИсточники:
Constant Source — источник, выдающий всегда одно и то же значение
Signal Source — источник, выдающий синусоидальный сигнал (для синтетического сигнала)
Audio Source/Sink — захват со звуковой карты или вывод в неё
File Source/Sink — чтение из файла (используйте Throttle для ограничения скорости чтения до нужного Sample Rate) и запись в файл
Wav File Source/Sink — чтение/запись WAV-файла
TCP/UDP Source/Sink — возможность стыковать проект с сетевым ПО посредством TCP или UDP сокетов
osmocom Source/Sink — приём данных от RTL-SDR или HackRF One или передача (для HackRF One)
Vector Source — источник последовательности чиселФильтры и модуляция:
Low/High/Band Pass Filter — низкочастотный/высокочастотный/полосовой фильтры
Frequency Xlating FIR Filter — совмещает сдвиг частоты и Low Pass фильтр для выделения нужной полосы частот
AM/FM/GFSK/… Mod/Demod — различные модуляторы и демодуляторыСемплинг:
Rational Resampler — позволяет преобразовать входящую последовательность отсчётов из одного Sample Rate в другой путём децимации и интерполяции (используется для «подгона» под нужный Sample Rate)
Throttle — ограничение скорости подачи отсчётов до нужного Sample Rate (если в проекте нед ни одного блока, ограничивающего скорость обработки)
Delay — задерживает поток на нужное число отсчётовUI:
WX GUI FFT Sink — графический вывод спектра сигнала
WX GUI Waterfall Sink — вывод спектральной мощности в режиме «водопада» (по оси X — частота, по Y — время, Z (цвет) — амплитуда). Полезно для поиска частоты излучения редко передающего источника в заданном диапазоне
WX GUI Constellation Sink — вывод фазовой диаграммы сигнала (разность фаз между колебаниями действительной и мнимой частей сигнала)
WX GUI Scope Sink — осциллографРазное:
Variable — переменная, позволяет использовать переменные вместо чисел в множестве блоков
Selector — мультиплексор, в сочетании с WX Slider позволит «на лету» переключать входы и выходы
Pack/Unpack K bits — преобразует из байтов 0/1 в последовательность из K бит и наоборот из байта в последовательность байт 0/1, соответствующую битам (удобно для кодирования/декодирования пакетов — байты 0/1 можно умножать на несущую, например)

Что ещё нужно знать о GNU Radio Companion (GRC)?


Все блоки в GRC, имеющие входы или выходы, требуют определения типа данных. Тип выхода одного блока должен быть таким же, как и входной тип связанного с ним блока. Выходные данные из одного блока можно передать в несколько блоков (т.е. нарисовать несколько связей). Но в один вход может входить только один поток!

Большинство блоков имеют минимальную документацию во вкладке в окне настроек, а так же в сносках к параметрам. Хотя обычно этого недостаточно. Немного устаревшая дока есть тут.

Все параметры у блоков могут быть выражениями Python. Т.е. вместо числа можно подставить формулу с участием переменных, определённых блоками Variable.

Часто хочется «покрутить» параметры проекта путём изменения значений переменных. Для этого используйте WX Slider или аналогичный компонент UI, используя его имя вместо нужной переменной. После запуска проекта в UI появится слайдер. Это удобно для перестройки частоты или параметров фильтров.

Ошибки в типе связей или в параметрах блока подсвечиваются красным. Они так же блокируют запуск проекта (в настройках блока, подсвеченного красным можно прочесть о проблеме). Есть run-time ошибки, которые проявляются уже при запуске проекта — лог внизу окна GRC подскажет, в чём проблема.

Некоторые блоки требуют целого числа в качестве параметра. Подстановка формулы может привести к несоответствию типов. Используйте Python-овскую функцию int () для преобразования к целому.

Для отключения блока используйте Disable/Enable. Это позволит не удалять блок, а просто исключить его из проекта на время с сохранением всех введённых параметров.

Все схемы в GRC преобразуются в скрипты на Python, что позволяет их потом изменять, а так же автоматизировать какие-то процессы минуя GRC.

Часто удобно сохранить звук/радио сигнал в файл для дальнейшего анализа. Для этого используйте блок File sink. Обратите внимание, что при чтении файла нужно помнить использованный формат данных при записи (тип числа), а так же Sample Rate. Рекомендую включать эти значения в имя файла помимо описания самого записываемого сигнала — это позволит не забыть, как его считать потом.

При проигрывании из файла обязательно используйте блок Throttle для ограничения скорости считывания. Это не требуется, если в схеме есть другой блок, который физически ограничивает скорость чтения данных. Например, блок Audio Sink итак ограничивает скорость подаваемых ему данных указанным в его настройках Sample Rate.

Для поиска нужного блока используйте значок лупы в панели инструментов. Двойной клик по названию в списке доступных блоком добавит блок в проект. Вместо этого можно перетащить его из списка в нужное место в проекте.

Старайтесь удобно располагать блоки в проекте. Это ни на что не влияет, кроме читабельности.

Надеюсь, данная статья побудит кого-то на эксперименты со звуком.

© Geektimes