Привет, Хабр.

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



И как и в предыдущем случае, мы сделаем это, не написав ни одной строчки кода, программа также будет кроссплатформенной, и сможет работать как под 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 не передавал только ленивый, а вот высокие частоты гораздо менее освоены. Так что, с платами типа LimeSDR или USRP можно проводить достаточно интересные опыты с приемом и передачей на сверхвысоких частотах.

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


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

Заключение


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

Комментарии (8)


  1. Naves
    30.05.2019 00:20

    Глупый вопрос, а как посмотреть графики из кдпв и последующий с Quadrature? Копался в gnu radio и не находил такой.


    1. DmitrySpb79 Автор
      30.05.2019 11:18

      Есть блоки Frequency Sink, Constellation Sink, Time Sink.


  1. Refridgerator
    30.05.2019 06:01

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


  1. dernuss
    30.05.2019 09:00

    Интересен приём и декодирование всяких CC1101, si4463, sx1276…


    1. DmitrySpb79 Автор
      30.05.2019 11:20

      Для Lora софтовый декодер вроде видел, сам пока не пробовал.


    1. jaha33
      02.06.2019 19:34

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


  1. VT100
    30.05.2019 21:44

    Что-то «созвездие» выглядит шумно. Или нет?


    1. DmitrySpb79 Автор
      30.05.2019 21:57

      Это похоже глюк «смотрелки» в GNU Radio. Например, непонятно откуда там точки около ноля берутся, вроде их не должно быть.
      Причем WX и QT-версии показывают разные результаты.