[recovery mode] Передача радиосигналов с помощью HackRF

HackRF — это уникальное устройство, предназначенное для энтузиастов и радиолюбителей, увлеченных изучением радиосигналов, и всего что с этим связано.
827ad71b16464e74bfdeaef86bd2f0ea.jpg
Уникальность HackRF в его технических параметрах — при цене около 300$, устройство позволяет не только принимать, но и передавать радиосигналы в диапазоне 1МГц-6ГГц с шириной полосы до 20МГц. Использование HackRF практически не освещалось на Хабре и Гиктаймс, пора восполнить этот пробел.

Что касается приема радиосигналов, то тут все просто, и практически не отличается от популярного 10-долларового RTL-SDR, о котором не писал только самый ленивый. Ставится нужная DLL, запускается программа, в общем-то и все. Кто еще не слышал о таком «чуде», могут почитать здесь же на сайте:
habrahabr.ru/post/149698
habrahabr.ru/post/149702
habrahabr.ru/post/280454
habrahabr.ru/post/200914

С приемом все достаточно просто и тривиально, технология дешевая и давно отработанная. С передачей все немного сложнее. Чем мы сейчас и займемся.

Низкий уровень — С/С++


Первый способ — общение напрямую с устройством. HackRF имеет библиотеку с открытым API, пример его использования можно посмотреть на сайте автора в утилите hackrf_transfer: github.com/mossmann/hackrf/blob/master/host/hackrf-tools/src/hackrf_transfer.c. Для тех, кто хоть раз работал со звуковой картой, принцип будет понятен — определенное число раз в секунду вызывается callback-функция, буфер для которой надо заполнить данными. Казалось бы все просто, но есть одно «но» — HackRF это SDR. Software Defined Radio. Это значит, что просто отправить WAV-файл в буфер не выйдет, ничего не передастся. Мы программно должны сформировать комплексный (IQ) сигнал для всей требуемой полосы приемника, чтобы его ЦАП в свою очередь, отправил «это» в эфир. Также необходимо задать ряд настроек (частота, усиление, полоса пропускания), что тоже можно посмотреть в исходнике.

HackRF поддерживает работу с различной шириной полосы пропускания, от 1 до 20МГц, и эту полосу необходимо сформировать программно. Мы этим заниматься не будем. Математика нужна достаточно серьезная, так что перейдем сразу ко второму способу — использованию пакета GNU Radio.

GNU Radio


GNU Radio — это открытый и бесплатный пакет программ, предназначенный для цифровой обработки сигналов в научных, образовательных или коммерческих целях. Система состоит из большого числа готовых блоков, имеющих интерфейс на языке Питон, сами блоки написаны на С++. В GNU Radio входит также визуальный редактор GNURadio Companion, позволяющий визуально соединять блоки в готовое «устройство», вообще не используя язык программирования.

Итак, устанавливаем драйвера для Hack RF, устанавливаем GNU Radio (он может быть поставлен на Linux или Mac OS, либо скачан как Linux Live CD), запускаем в консоли «gnuradio-companion». Теперь все готово, можно приступить к передаче.

Формально HackRF может передать что угодно в пределах своей полосы пропускания, например зная протокол передачи данных, можно рулить радиоуправляемой машинкой или управлять дверным звонком. Рассмотрим самые тривиальные примеры: амплитудную (AM) и частотную (FM) модуляцию.

Частотная модуляция
Для передачи в режиме FM запустим gnuradio-companion и создадим в программе такой граф.
cdac5bf6f16f43bd9d951aeedbb871aa.png

Его структура довольно проста. В качестве источника был выбран WAV-файл с частотой дискретизации 44100, блок Multiply Const умножает сигнал на 2 (исходный звук был довольно-таки тихий), следующий блок WBFM Transmit и делает всю «магию» — преобразует наш вещественный сигнал с частотой 44100 в комплексный сигнал с частотой дискретизации 88200. Следующий блок Resampler повышает частоту дискретизации до выбранных для этого примера 2.4МГц. Частота передачи установлена на 87.5МГц, это была самая низкая частота FM-радио, поддерживаемая моим смартфоном, и она была свободна от FM-станций.

Результат — запускаем граф в GNU Radio, на HackRF загорается красный светодиод TX, в смартфоне слышен наш файл.

Интересно посмотреть как реально это работает. В консоли GNU Radio можно видеть следующий вывод:
Executing /opt/local/Library/Frameworks/Python.framework/Versions/2.7/Resources/Python.app/Contents/MacOS/Python -u /Users/XXXX/Documents/Projects/Gnuradio/top_block.py

Его содержимое — готовая программа на Питоне, реализующая нашу передачу!

Исходный код
##################################################
# GNU Radio Python Flow Graph
# Title: Top Block
# Generated: Sun May  1 18:29:52 2016
##################################################

if __name__ == '__main__':
    import ctypes
    import sys
    if sys.platform.startswith('linux'):
        try:
            x11 = ctypes.cdll.LoadLibrary('libX11.so')
            x11.XInitThreads()
        except:
            print "Warning: failed to XInitThreads()"

from gnuradio import analog
from gnuradio import blocks
from gnuradio import eng_notation
from gnuradio import filter
from gnuradio import gr
from gnuradio.eng_option import eng_option
from gnuradio.filter import firdes
from grc_gnuradio import wxgui as grc_wxgui
from optparse import OptionParser
import osmosdr
import time
import wx


class top_block(grc_wxgui.top_block_gui):

    def __init__(self):
        grc_wxgui.top_block_gui.__init__(self, title="Top Block")

        ##################################################
        # Variables
        ##################################################
        self.samp_rate = samp_rate = 2.4e6

        ##################################################
        # Blocks
        ##################################################
        self.rational_resampler_xxx_1 = filter.rational_resampler_ccc(
                interpolation=int(samp_rate),
                decimation=88200,
                taps=None,
                fractional_bw=None,
        )
        self.osmosdr_sink_0 = osmosdr.sink( args="numchan=" + str(1) + " " + "" )
        self.osmosdr_sink_0.set_sample_rate(samp_rate)
        self.osmosdr_sink_0.set_center_freq(87.5e6, 0)
        self.osmosdr_sink_0.set_freq_corr(6, 0)
        self.osmosdr_sink_0.set_gain(10, 0)
        self.osmosdr_sink_0.set_if_gain(30, 0)
        self.osmosdr_sink_0.set_bb_gain(20, 0)
        self.osmosdr_sink_0.set_antenna("", 0)
        self.osmosdr_sink_0.set_bandwidth(0, 0)
          
        self.blocks_wavfile_source_0 = blocks.wavfile_source("/Users/XXXX/Documents/Projects/sample.wav", True)
        self.blocks_multiply_const_vxx_0 = blocks.multiply_const_vff((2, ))
        self.analog_wfm_tx_0 = analog.wfm_tx(
                audio_rate=44100,
                quad_rate=88200,
                tau=75e-6,
                max_dev=75e3,
        )

        ##################################################
        # Connections
        ##################################################
        self.connect((self.analog_wfm_tx_0, 0), (self.rational_resampler_xxx_1, 0))    
        self.connect((self.blocks_multiply_const_vxx_0, 0), (self.analog_wfm_tx_0, 0))    
        self.connect((self.blocks_wavfile_source_0, 0), (self.blocks_multiply_const_vxx_0, 0))    
        self.connect((self.rational_resampler_xxx_1, 0), (self.osmosdr_sink_0, 0))    

    def get_samp_rate(self):
        return self.samp_rate

    def set_samp_rate(self, samp_rate):
        self.samp_rate = samp_rate
        self.osmosdr_sink_0.set_sample_rate(self.samp_rate)


def main(top_block_cls=top_block, options=None):

    tb = top_block_cls()
    tb.Start(True)
    tb.Wait()


if __name__ == '__main__':
    main()



Ее можно сохранить в отдельную папку и использовать как угодно, модифицировать и пр. Это достаточно удобно, т.к. позволяет например, дописать свой UI, сделать запуск по расписанию, в общем, все ограничено только фантазией программиста.

Немного модифицируем граф, чтобы запускать программу в режиме «узкой ЧМ», которая используется в рациях и радионянях:
a121f69238ab4965bd032b2a32de8a1b.png

Отличие небольшое — мы установили блок NBFM, а частоту передачи установили в 433.075МГц — первый канал LPD-диапазона. Включаем рацию, и слышим звук. Кстати, GNU Radio позволяет использовать в качестве источника не только WAV-файл, но и микрофон, так что программу легко переделать для передачи голосовых сообщений.

Амплитудная модуляция
Для тех у кого остался бабушкин приемник КВ-диапазона (хотя лучше конечно хотя бы Degen 1103 или IC-R9500), будет интересно попробовать режим AM. Для этого создаем следующий граф:
49dd0e4d6170458c941351596080daf3.png

Здесь для конвертации данных в комплексный сигнал мы используем блок преобразования Гильберта. В остальном суть не изменилась, сигнал ресемплируется до нужной частоты дискретизации и отправляется в HackRF.

Еще больше SDR
Напоследок покажем, что HackRF может куда больше, чем просто рация из «Связного» ценой в 1000р. Это ведь полноценное Software Defined Radio, на котором можно передать что угодно. Чтобы показать легкость работы с «цифрой» в GNU Radio, дополним граф FM-трансляции так, чтобы HackRF передавал информацию одновременно на 2х частотах.
6b4a9fdb78bd42318b7225bbbac543fc.png

Здесь мы добавляем второй файл, повторяем ту же самую структуру. Но полученные данные умножаем на комплексный синусоидальный сигнал с частотой 125КГц, что эквивалентно сдвигу частоты на эти 125КГц, в итоге второй поток данных будет транслироваться на частоте 433075+125 = 433200, что соответствует 6 му каналу LPD. Теперь HackRF передает одновременно 2 звуковых файла на 2х разных частотах. Можно добавить и 3й, все ограничено лишь шириной полосы пропускания и мощностью компьютера.

На этом все. Заинтересовавшиеся темой GNU Radio могут найти туториалы в сети, их довольно-таки много.

Напоследок. HackRF не является полноценной радиостанцией, его мощность слишком мала и составляет около 20 мВт, да и КПД штатной антенны менее 1%. Это скорее устройство для экспериментов с сигналами в «ближней зоне», в пределах офиса или квартиры. Если же кто-то захочет экспериментировать с более мощными сигналами, обязательно стоит соблюдать правила распределения радиочастотного спектра, чтобы не мешать другим.

© Geektimes