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

Привет Хабр.

В предыдущей части про передачу в GNU Radio был задан вопрос о том, можно ли декодировать протокол LoRa (передача данных для устройств с низким энергопотреблением) с помощью SDR. Мне эта тема показалась интересной, тем более что и сам сигнал у LoRa довольно-таки необычный — так называемая Chirp Spread Spectrum modulation, или «модуляция чирпами».

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

Как это работает, продолжение под катом.

Кстати, дословный перевод Chirp modulation звучал бы как «модуляция чириканием», но это звучит уж совсем сюрреалистично, так что лучше оставлю простое слово «чирп».

Модуляция LoRa

Как было сказано выше, при передаче LoRa используется способ модуляции при помощи «чирпов», кстати запатентованный компанией Semtech. Если кто хочет подробностей реализации с формулами, можно почитать PDF на сайте semtech или здесь, ну а если совсем грубо, то один «чирп» — это одно изменение частоты, такими изменениями и кодируется битовый поток, как показано на картинке выше. Параметрами сигнала в LoRa являются SF (spreading factor — по сути, длительность одного «чирпа») и bandwidth — ширина полосы передачи. Параметр SF задается предопределенными значениями SF7 — SF12, где 7 самый быстрый, а 12 — самый медленный режим (для примера можно посмотреть картинку с иллюстрацией разных скоростей «чирпования» с researchgate).

Очевидно, чем меньше длина «чирпа» и чем шире полоса, тем больше можно получить скорость передачи. Все это связано примерно такой таблицей:

С точки зрения дальности и помехозащищенности выгодно передавать медленно и печально, но при этом мы во-первых, теряем в скорости, во-вторых, проигрываем во времени, а по правилам LoRa, устройство может передавать не более 1% времени, чтобы не мешать другим устройствам. Так что выбор оптимальной скорости передачи для малопотребляющих устройств задача тоже не простая.

С общим принципом надеюсь, ясно, теперь перейдем к SDR и декодированию.

Железо

Для тестирования я использовал LoRa Click RN2483 и Arduino M0, просто потому что они были в наличии.

Это достаточно удобный форм-фактор для прототипирования, т.к. позволяет легко заменить плату одну на другую без пайки (в этом формате, называемом MikroBUS доступно много разной периферии).

Код в черновом варианте, не претендующий на production, добавлен под спойлер. В качестве теста передается значение «1234».

rn2483_tx.ino

// RN2483 Modem and LoRa Click test TX. Tested with Arduino M0  int reset = A2; int rts = 9; // CS int cts = 3; // INT  // the setup routine runs once when you press reset: void setup() {     Serial1.begin(57600); // Serial port to radio      // output LED pin     pinMode(LED_BUILTIN, OUTPUT);          pinMode(cts, INPUT);          pinMode(rts, OUTPUT);     digitalWrite(rts, HIGH);      // Reset rn2483     pinMode(reset, OUTPUT);     digitalWrite(reset, LOW);     delay(100);     digitalWrite(reset, HIGH);      delay(100);      sendCommand("sys get verrn");     sendCommand("sys get hweuirn");      sendCommand("mac pausern");     sendCommand("radio set mod lorarn");     sendCommand("radio set pwr -3rn"); // the transceiver output power, from -3 to 15     sendCommand("radio set sf sf8rn"); // sf7..sf12, sf7 the fastest spreading factor but gives the shortest range     // sendCommand("mac set dr 0rn"); // data rate: 0-4, 4 faster     sendCommand("radio set freq 869100000rn");     // sendCommand("radio set afcbw 41.7rn");     sendCommand("radio set rxbw 125rn");     // sendCommand("radio set prlen 8rn");     sendCommand("radio set crc onrn");     // sendCommand("radio set iqi offrn");     sendCommand("radio set cr 4/8rn");     // sendCommand("radio set wdt 60000rn"); // disable for continuous reception     // sendCommand("radio set sync 12rn");     sendCommand("radio set bw 125rn"); }  void sendCommand(const char *cmd)  {     Serial1.print(cmd);     String incoming = Serial1.readString();     // SerialUSB.print(cmd);      // SerialUSB.println(incoming);  }  // the loop routine runs over and over again forever: void loop() {     char data[64] = {0};      //  hexadecimal value representing the data to be transmitted, from 0 to 255 bytes for LoRa modulation and from 0 to 64 bytes for FSK modulation.     sprintf(data, "radio tx 1234rn");     sendCommand(data);      if (msg_num > 10000) msg_num=0;      digitalWrite(LED_BUILTIN, 1);     delay(400);     digitalWrite(LED_BUILTIN, 0);     delay(600); }

Кстати, максимальная заявленная дальность передачи для RN2483 составляет до 15км, на практике, при наличии одноэтажной застройки сигнал пропадает уже за 1км, и может быть не более 100м в городских «муравейниках».

Запускаем модем, и приступаем к декодированию.

Декодирование

В самом GNU Radio поддержки LoRa нет, так что придется использовать сторонние компоненты. Их нашлось всего два, и к сожалению, оба автора не проявили никакой фантазии в названии, и назвали их совершенно одинаково — gr-lora (https://github.com/rpp0/gr-lora и https://github.com/BastilleResearch/gr-lora соответственно).

rpp0/gr-lora

Скачать исходники декодера можно с github, сборка стандартна и затруднений не вызывает:

git clone https://github.com/rpp0/gr-lora.git cd gr-lora mkdir build cd build cmake .. make && sudo make install && sudo ldconfig

После установки в GNU Radio появляются дополнительные блоки, из которых несложно собрать декодер.

Блоки в GNU Radio стандартизированы, так что можно использовать любой приемник, например RTL-SDR. Я использовал SDRPlay. Для вывода данных в консоль использовалась простая программа на Python.

udp_receive.py

import socket  UDP_IP = "127.0.0.1" UDP_PORT = 40868  sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind((UDP_IP, UDP_PORT)) sock.settimeout(0.5)  while True:     try:         data, addr = sock.recvfrom(128) # buffer size is 1024 bytes         print("Msg:", ' '.join('{:02x}'.format(x) for x in data))      except socket.timeout:         pass 

В качестве параметров декодера необходимо указать ширину полосы передачи, spreading factor, центральную частоту SDR и частоту приема.

Результат работы показан на рисунке.

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

BastilleResearch/gr-lora

Этот модуль примечателен тем, что может работать не только на прием, но и на передачу. Установка примерно такая же: компонент нужно собрать из исходников.

git clone git://github.com/BastilleResearch/gr-lora.git cd gr-lora mkdir build cd build cmake .. make && sudo make install && sudo ldconfig

Connection Graph для данного декодера показан на рисунке.

Как можно видеть, блоков тут побольше. Rotator и Polyphase Resampler выделяют нужную частоту и обрезают лишнее, Demodulator преобразует «чирпы» в бинарный код (на выходе получается последовательность вроде «17 00 3e 00 38 00 2f 00 01 00 39 00 2c 00 30 00 c6 00 18 00 7e 00 d5 00 85 00 e9 00 d8 00 67 00 c4 00»), а Decoder формирует окончательный пакет.

К сожалению, нормально оно так и не заработало. Декодирование определенно работает, во время работы модема данные появляются, но принимаемые сообщения не имеют ничего общего с передаваемыми.

Причину я так и не понял, то ли где-то ошибся в настройках, то ли этот декодер совместим только со своим же кодером. Желающие могут проверить самостоятельно с помощью Channel Model.

LoRaWAN

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

Заключение

Как можно видеть, декодирование LoRa средствами SDR вполне возможно. Конечно, реальный gateway делать на базе SDR вряд ли целесообразно — его чувствительность будет хуже чувствительности «настоящих» модемов, которые специально рассчитаны на прием слабых сигналов, и имеют более узкополосные фильтры и LNA. Но для тестирования или исследования это может быть вполне интересно.

Для тех кто захочет повторить эксперименты, файлы GNU Radio под спойлером.

receive1.grc

     Mon Jun  3 09:39:45 2019        options            author                        window_size                        category       [GRC Hier Blocks]                 comment                        description                        _enabled       True                 _coordinate       (8, 8)                 _rotation       0                 generate_options       wx_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       (760, 12)                 _rotation       0                 id       bw                 value       125000.0                variable            comment                        _enabled       1                 _coordinate       (936, 12)                 _rotation       0                 id       code_rate                 value       4                variable            comment                        _enabled       1                 _coordinate       (848, 12)                 _rotation       0                 id       header                 value       True                variable            comment                        _enabled       1                 _coordinate       (680, 12)                 _rotation       0                 id       ldr                 value       True                variable            comment                        _enabled       True                 _coordinate       (400, 12)                 _rotation       0                 id       offset                 value       -100000.0                variable            comment                        _enabled       True                 _coordinate       (8, 76)                 _rotation       0                 id       samp_rate                 value       1000000                variable            comment                        _enabled       1                 _coordinate       (544, 12)                 _rotation       0                 id       spreading_factor                 value       8                blocks_rotator_cc            alias                        comment                        affinity                        _enabled       1                 _coordinate       (416, 252)                 _rotation       0                 id       blocks_rotator_cc_0                 maxoutbuf       0                 minoutbuf       0                 phase_inc       (2 * math.pi * offset) / samp_rate                blocks_socket_pdu            alias                        comment                        affinity                        _enabled       1                 _coordinate       (944, 452)                 _rotation       0                 host       127.0.0.1                 id       blocks_socket_pdu_0                 mtu       10000                 maxoutbuf       0                 minoutbuf       0                 port       40868                 tcp_no_delay       False                 type       "UDP_CLIENT"                import            alias                        comment                        _enabled       True                 _coordinate       (296, 12)                 _rotation       0                 id       import_0                 import       import math                lora_decode            alias                        code_rate       code_rate                 comment                        affinity                        _enabled       1                 header       header                 _coordinate       (648, 452)                 _rotation       0                 id       lora_decode_0                 low_data_rate       ldr                 maxoutbuf       0                 minoutbuf       0                 spreading_factor       spreading_factor                lora_demod            alias                        comment                        affinity                        _enabled       1                 fft_factor       2                 beta       25.0                 _coordinate       (384, 452)                 _rotation       0                 id       lora_demod_0                 low_data_rate       ldr                 maxoutbuf       0                 minoutbuf       0                 spreading_factor       spreading_factor                pfb_arb_resampler_xxx            alias                        comment                        affinity                        _enabled       1                 _coordinate       (656, 292)                 _rotation       0                 id       pfb_arb_resampler_xxx_0                 maxoutbuf       0                 minoutbuf       0                 nfilts       32                 rrate       bw/samp_rate                 samp_delay       0                 atten       100                 taps                        type       ccf                sdrplay_rsp2_source            agc_enabled       False                 antenna       'A'                 bw       400                 alias                        comment                        affinity                        dc_offset_mode       True                 debug_enabled       False                 device_serial       '0'                 _enabled       True                 _coordinate       (144, 196)                 _rotation       0                 id       sdrplay_rsp2_source_0                 if_atten_db       30                 ifType       0                 iq_balance_mode       True                 lna_atten_step       3                 lo_mode       1                 maxoutbuf       0                 minoutbuf       0                 rf_freq       869.0e6                 sample_rate       samp_rate                wxgui_fftsink2            avg_alpha       0                 average       False                 baseband_freq       0                 alias                        comment                        affinity                        _enabled       True                 fft_size       1024                 freqvar       None                 _coordinate       (1000, 116)                 _rotation       0                 grid_pos                        id       wxgui_fftsink2_0                 notebook                        peak_hold       False                 ref_level       0                 ref_scale       2.0                 fft_rate       15                 samp_rate       samp_rate                 title       FFT Plot                 type       complex                 win_size                        win       None                 y_divs       10                 y_per_div       10                blocks_rotator_cc_0     pfb_arb_resampler_xxx_0     0     0           blocks_rotator_cc_0     wxgui_fftsink2_0     0     0           lora_decode_0     blocks_socket_pdu_0     out     pdus           lora_demod_0     lora_decode_0     out     in           pfb_arb_resampler_xxx_0     lora_demod_0     0     0           sdrplay_rsp2_source_0     blocks_rotator_cc_0     0     0     

receive2.grc

     Mon Jun  3 09:39:45 2019        options            author                        window_size                        category       [GRC Hier Blocks]                 comment                        description                        _enabled       True                 _coordinate       (8, 8)                 _rotation       0                 generate_options       wx_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       (184, 12)                 _rotation       0                 id       samp_rate                 value       1000000                lora_lora_receiver            bandwidth       125000                 alias                        crc       True                 center_freq       869e6                 channel_list       [869.1e6]                 cr       4                 comment                        conj       False                 affinity                        decimation       1                 disable_channelization       False                 disable_drift_correction       False                 _enabled       True                 _coordinate       (456, 332)                 _rotation       0                 id       lora_lora_receiver_0                 implicit       False                 maxoutbuf       0                 minoutbuf       0                 reduced_rate       False                 samp_rate       1e6                 sf       8                lora_message_socket_sink            alias                        comment                        affinity                        _enabled       True                 _coordinate       (696, 364)                 _rotation       0                 id       lora_message_socket_sink_0                 ip       127.0.0.1                 layer       1                 port       40868                sdrplay_rsp2_source            agc_enabled       False                 antenna       'A'                 bw       400                 alias                        comment                        affinity                        dc_offset_mode       True                 debug_enabled       False                 device_serial       '0'                 _enabled       True                 _coordinate       (72, 148)                 _rotation       0                 id       sdrplay_rsp2_source_0                 if_atten_db       30                 ifType       0                 iq_balance_mode       True                 lna_atten_step       3                 lo_mode       1                 maxoutbuf       0                 minoutbuf       0                 rf_freq       869.0e6                 sample_rate       samp_rate                wxgui_fftsink2            avg_alpha       0                 average       True                 baseband_freq       0                 alias                        comment                        affinity                        _enabled       True                 fft_size       1024                 freqvar       None                 _coordinate       (688, 108)                 _rotation       0                 grid_pos                        id       wxgui_fftsink2_0                 notebook                        peak_hold       True                 ref_level       0                 ref_scale       2.0                 fft_rate       15                 samp_rate       samp_rate                 title       FFT Plot                 type       complex                 win_size                        win       None                 y_divs       10                 y_per_div       10                lora_lora_receiver_0     lora_message_socket_sink_0     frames     in           sdrplay_rsp2_source_0     lora_lora_receiver_0     0     0           sdrplay_rsp2_source_0     wxgui_fftsink2_0     0     0     

Всем удачных экспериментов.

 
Источник

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