Software Defined Radio — как это работает? Часть 6

Привет Хабр.

В предыдущей части мы рассмотрели возможность передачи простых сигналов с помощью GNU Radio. Сейчас мы пойдем дальше, и посмотрим, как передать что-нибудь посложнее. Начнем с радиолюбительских сигналов WSPR, а затем создадим работающий программный QAM-модем.

Software Defined Radio — как это работает? Часть 6

И как и в предыдущем случае, мы сделаем это, не написав ни одной строчки кода, программа также будет кроссплатформенной, и сможет работать как под OSX/Linux, так и под Windows. Я также покажу, как отлаживать модем средствами GNU Radio, вообще не имея никакого «железа».

Продолжение под катом.

Для тех, кто не пользовался GNU Radio, рекомендуется прочитать части 4 и 5, где описывались принципы работы с программой.

WSPR

Начнем с более простого, с WSPR — этот вид связи специально делался для тестов распространения слабых сигналов, т.е. то что нам и нужно — уровень мощности устройств типа LimeSDR не более 100мВт. Сигнал WSPR передается о очень малой скоростью (2 минуты на сообщение из примерно 30 байт) в очень узкой полосе, что позволяет принимать его даже ниже уровня шумов. Передадим такой сигнал с помощью GNU Radio.

Для начала сигнал надо записать. Для этого возьмем программу WSJT, выставим все необходимые параметры (мощность, позывной, местоположение и пр). Укажем в настройках в качестве выходного аудиоустройства Virtual Audio Cable, и запишем сигнал в WAV. Паузы по краям нужно обрезать в любом редакторе (например Cool Edit), в итоге у нас должен получиться файл длительностью примерно в 2 минуты.

Теперь создаем граф в GNU Radio Companion.

Данный способ не претендует на максимальную эффективность, зато он довольно простой и понятный. Сигнал WSPR изначально находится на частоте 1500Гц, записанный wav-файл имеет частоту дискретизации 22050с/сек. Сначала мы ресемплируем сигнал в 57/5 раз, чтобы привести частоту дискретизации к требуемым 250.000с/сек. Затем мы сдвигаем частоту вверх на 10КГц (полезный сигнал при этом будет на частоте 11.5КГц), переводим сигнал в комплексный вид, требуемый приемнику, и вырезаем фильтром лишнее, оставляя частоты 11-12КГц.

Сигналы WSPR привязаны ко времени, и передаются каждую четную минуту (0:00, 0:02 и пр). Я запускал передачу в GNU Radio вручную, «на глаз» определяя интервал по часам, желающие могут дописать скрипт в Python для автоматического начала передачи.

Ждем нужное время, включаем передатчик, приемник, и проверяем результат.

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

Интересно заметить, что на 432МГц дрифт частоты уже довольно заметен, хотя сигнал еще декодируется. А вот на частоте 1.3ГГц дрифт становится настолько большим, что WSPR уже не принимается — к SDR нужен внешний опорный генератор с более стабильным сигналом (или хотя бы программная коррекция частоты при передаче, хотя это менее удобно).

Если SDR позволяет передавать на низких частотах, то можно попробовать передачу и на КВ-диапазоне. Таким образом с HackRF удавалось передать сигнал на 1000км на 14МГц с комнатной антенны, что можно считать неплохим результатом. Высокие частоты (433МГц и 1.3ГГц) пожалуй даже более интересны для опытов, но сигналы передаются только в прямой видимости, так что для таких экспериментов нужен второй участник на приемной стороне. Второй плюс таких тестов — на КВ в wspr не передавал только ленивый, а вот высокие частоты гораздо менее освоены.

QAM Модем

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

Рассмотрим первую часть — передатчик.

Как можно видеть, мы читаем данные из файла data.txt, затем с частотой семплирования 25КГц посылаем данные на энкодер пакетов, который преобразует поток в 4х-битный код. Этот поток поступает на квадратурный модулятор, затем частота семплирования увеличивается до частоты передатчика 250КГц, и сигнал сдвигается вверх на 80КГц (у многих приемников есть пик на нулевой частоте, и это будет мешать). Компонент Constellation Rect задает параметры модуляции — количество символов и сдвиг фаз и амплитуд.

Первая часть готова. Запускаем «передачу» и видим наш сигнал.

Мы можем протестировать наш передатчик, даже не имея никакой аппаратуры — для этого есть специальный блок Channel Model — модель канала связи. Там можно задать шум, сдвиг частоты и пр.

Вот так выглядит наш сигнал до и после передачи. Кстати, как можно видеть все «гармоники» передатчика ниже 120Дб ушли гораздо ниже уровня шумов.

Теперь прием. Фактически то же самое, только в обратном порядке.

Отдельно можно остановиться на последнем блоке UDP Sink. Непонятно почему, но в GNU Radio нет никакого компонента для просмотра текстовых данных. Поэтому мы просто посылаем данные по UDP на любой локальный порт (я выбрал 999).

Для приема напишем несложную программу на Python.

import socket  UDP_IP = "127.0.0.1" UDP_PORT = 999  sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP sock.bind((UDP_IP, UDP_PORT)) sock.settimeout(1.0)  while True:   try:     data, addr = sock.recvfrom(64) # buffer size is 64 bytes     print("Msg:", data)    except socket.timeout:    pass

Результат: запускаем скрипт, запускаем GNU Radio, и видим в консоли принятые сообщения.

Как можно видеть, все работает, и ни приемника, ни антенн можно не иметь 🙂

Для тех, кто захочет повторить эксперименты, grc-файл проекта под спойлером. Должно работать и под Linux и под Windows.

qam_test.grc

     Mon May 27 21:52:42 2019        options            author                        window_size                        category       [GRC Hier Blocks]                 comment                        description                        _enabled       True                 _coordinate       (8, 8)                 _rotation       0                 generate_options       qt_gui                 hier_block_src_path       .:                 id       top_block                 max_nouts       0                 qt_qss_theme                        realtime_scheduling                        run_command       {python} -u {filename}                 run_options       prompt                 run       True                 thread_safe_setters                        title                       variable            comment                        _enabled       True                 _coordinate       (1144, 172)                 _rotation       0                 id       excess_bw                 value       0.35                variable            comment                        _enabled       True                 _coordinate       (1104, 436)                 _rotation       0                 id       nfilts                 value       32                variable            comment                        _enabled       True                 _coordinate       (1096, 588)                 _rotation       0                 id       nfilts_0                 value       32                variable_constellation_rect            comment                        const_points       [0.707+0.707j, -0.707+0.707j, -0.707-0.707j, 0.707-0.707j]                 _enabled       True                 _coordinate       (1104, 16)                 _rotation       0                 id       qpsk                 imag_sect       2                 real_sect       2                 rot_sym       4                 soft_dec_lut       None                 precision       8                 sym_map       [0, 1, 2, 3]                 w_imag_sect       1                 w_real_sect       1                variable            comment                        _enabled       True                 _coordinate       (1104, 372)                 _rotation       0                 id       rrc_taps                 value       firdes.root_raised_cosine(nfilts, nfilts, 1.0/float(sps), 0.35, 45*nfilts)                variable            comment                        _enabled       True                 _coordinate       (1112, 508)                 _rotation       0                 id       rrc_taps_0                 value       firdes.root_raised_cosine(nfilts, nfilts, 1.0/float(sps), 0.35, 45*nfilts)                variable            comment                        _enabled       True                 _coordinate       (168, 12)                 _rotation       0                 id       samp_rate                 value       250000                variable            comment                        _enabled       True                 _coordinate       (1144, 244)                 _rotation       0                 id       sps                 value       4                variable            comment                        _enabled       True                 _coordinate       (1104, 308)                 _rotation       0                 id       timing_loop_bw                 value       6.28/100.0                analog_sig_source_x            amp       1                 alias                        comment                        affinity                        _enabled       True                 freq       80000                 _coordinate       (664, 20)                 _rotation       0                 id       analog_sig_source_x_0                 maxoutbuf       0                 minoutbuf       0                 offset       0                 type       complex                 samp_rate       samp_rate                 waveform       analog.GR_COS_WAVE                analog_sig_source_x            amp       1                 alias                        comment                        affinity                        _enabled       True                 freq       -80000                 _coordinate       (48, 540)                 _rotation       0                 id       analog_sig_source_x_1                 maxoutbuf       0                 minoutbuf       0                 offset       0                 type       complex                 samp_rate       samp_rate                 waveform       analog.GR_COS_WAVE                blks2_packet_decoder            access_code                        alias                        comment                        affinity                        _enabled       1                 _coordinate       (296, 676)                 _rotation       0                 id       blks2_packet_decoder_0                 maxoutbuf       0                 minoutbuf       0                 type       byte                 threshold       -1                blks2_packet_encoder            access_code                        bits_per_symbol       4                 alias                        comment                        affinity                        _enabled       1                 _coordinate       (224, 76)                 _rotation       0                 id       blks2_packet_encoder_0                 type       byte                 maxoutbuf       0                 minoutbuf       0                 pad_for_usrp       True                 payload_length       0                 preamble                        samples_per_symbol       4                blocks_file_source            alias                        comment                        affinity                        _enabled       1                 file       D:MyProjectsGNURadiodata.txt                 _coordinate       (8, 92)                 _rotation       0                 id       blocks_file_source_0                 maxoutbuf       0                 minoutbuf       0                 type       byte                 repeat       True                 vlen       1                blocks_multiply_xx            alias                        comment                        affinity                        _enabled       True                 _coordinate       (920, 88)                 _rotation       0                 id       blocks_multiply_xx_0                 type       complex                 maxoutbuf       0                 minoutbuf       0                 num_inputs       2                 vlen       1                blocks_multiply_xx            alias                        comment                        affinity                        _enabled       True                 _coordinate       (224, 496)                 _rotation       0                 id       blocks_multiply_xx_1                 type       complex                 maxoutbuf       0                 minoutbuf       0                 num_inputs       2                 vlen       1                blocks_throttle            alias                        comment                        affinity                        _enabled       1                 _coordinate       (96, 196)                 _rotation       0                 id       blocks_throttle_1                 ignoretag       True                 maxoutbuf       0                 minoutbuf       0                 samples_per_second       25000                 type       byte                 vlen       1                blocks_udp_sink            alias                        comment                        affinity                        ipaddr       127.0.0.1                 port       999                 _enabled       1                 _coordinate       (680, 660)                 _rotation       0                 id       blocks_udp_sink_0                 type       byte                 psize       64                 eof       True                 vlen       1                channels_channel_model            alias                        block_tags       False                 comment                        affinity                        _enabled       1                 epsilon       1.0                 freq_offset       0.0                 _coordinate       (504, 284)                 _rotation       180                 id       channels_channel_model_0                 maxoutbuf       0                 minoutbuf       0                 noise_voltage       0.1                 seed       0                 taps       1.0 + 1.0j                digital_qam_demod            alias                        comment                        affinity                        differential       True                 _enabled       1                 excess_bw       0.35                 freq_bw       6.28/100.0                 _coordinate       (672, 456)                 _rotation       0                 mod_code       "gray"                 id       digital_qam_demod_0                 log       False                 maxoutbuf       0                 minoutbuf       0                 constellation_points       4                 phase_bw       6.28/100.0                 samples_per_symbol       4                 timing_bw       6.28/100.0                 verbose       False                digital_qam_mod            alias                        comment                        affinity                        differential       True                 _enabled       True                 excess_bw       0.35                 _coordinate       (384, 116)                 _rotation       0                 mod_code       "gray"                 id       digital_qam_mod_0                 log       False                 maxoutbuf       0                 minoutbuf       0                 constellation_points       4                 samples_per_symbol       4                 verbose       False                low_pass_filter            beta       6.76                 alias                        comment                        affinity                        cutoff_freq       12000                 decim       1                 _enabled       True                 type       fir_filter_ccf                 _coordinate       (320, 460)                 _rotation       0                 gain       1                 id       low_pass_filter_0                 interp       1                 maxoutbuf       0                 minoutbuf       0                 samp_rate       samp_rate                 width       1000                 win       firdes.WIN_HAMMING                qtgui_const_sink_x            autoscale       False                 axislabels       True                 alias                        comment                        affinity                        _enabled       1                 _coordinate       (456, 20)                 gui_hint                        _rotation       0                 grid       False                 id       qtgui_const_sink_x_0                 legend       True                 alpha1       1.0                 color1       "blue"                 label1                        marker1       0                 style1       0                 width1       1                 alpha10       1.0                 color10       "red"                 label10                        marker10       0                 style10       0                 width10       1                 alpha2       1.0                 color2       "red"                 label2                        marker2       0                 style2       0                 width2       1                 alpha3       1.0                 color3       "red"                 label3                        marker3       0                 style3       0                 width3       1                 alpha4       1.0                 color4       "red"                 label4                        marker4       0                 style4       0                 width4       1                 alpha5       1.0                 color5       "red"                 label5                        marker5       0                 style5       0                 width5       1                 alpha6       1.0                 color6       "red"                 label6                        marker6       0                 style6       0                 width6       1                 alpha7       1.0                 color7       "red"                 label7                        marker7       0                 style7       0                 width7       1                 alpha8       1.0                 color8       "red"                 label8                        marker8       0                 style8       0                 width8       1                 alpha9       1.0                 color9       "red"                 label9                        marker9       0                 style9       0                 width9       1                 name       ""                 nconnections       1                 size       1024                 tr_chan       0                 tr_level       0.0                 tr_mode       qtgui.TRIG_MODE_FREE                 tr_slope       qtgui.TRIG_SLOPE_POS                 tr_tag       ""                 type       complex                 update_time       0.10                 xmax       2                 xmin       -2                 ymax       2                 ymin       -2                rational_resampler_xxx            alias                        comment                        affinity                        decim       1                 _enabled       True                 fbw       0                 _coordinate       (648, 148)                 _rotation       0                 id       rational_resampler_xxx_0                 interp       10                 maxoutbuf       0                 minoutbuf       0                 taps                        type       ccc                rational_resampler_xxx            alias                        comment                        affinity                        decim       10                 _enabled       True                 fbw       0                 _coordinate       (480, 484)                 _rotation       0                 id       rational_resampler_xxx_0_0                 interp       1                 maxoutbuf       0                 minoutbuf       0                 taps                        type       ccc                analog_sig_source_x_0     blocks_multiply_xx_0     0     0           analog_sig_source_x_1     blocks_multiply_xx_1     0     1           blks2_packet_decoder_0     blocks_udp_sink_0     0     0           blks2_packet_encoder_0     digital_qam_mod_0     0     0           blocks_file_source_0     blocks_throttle_1     0     0           blocks_multiply_xx_0     channels_channel_model_0     0     0           blocks_multiply_xx_1     low_pass_filter_0     0     0           blocks_throttle_1     blks2_packet_encoder_0     0     0           channels_channel_model_0     blocks_multiply_xx_1     0     0           digital_qam_demod_0     blks2_packet_decoder_0     0     0           digital_qam_mod_0     qtgui_const_sink_x_0     0     0           digital_qam_mod_0     rational_resampler_xxx_0     0     0           low_pass_filter_0     rational_resampler_xxx_0_0     0     0           rational_resampler_xxx_0     blocks_multiply_xx_0     0     1           rational_resampler_xxx_0_0     digital_qam_demod_0     0     0     

Хорошее руководство по созданию более сложного модема есть на сайте GNU Radio, но они используют custom block для демодуляции, запустить который удалось только под Linux. В вышеприведенном примере с этим проблем нет.

Заключение

Как можно видеть, GNU Radio — довольно интересная программа для разной работы с сигналами, в которой можно делать много разных интересных вещей. Если у аудитории не пропадет интерес (у меня есть чувство, что я углубляюсь в узкие темы, которые большинству уже мало интересны), можно попробовать рассмотреть передачу чего-то более интересного, например видео.

 
Источник

Читайте также