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

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

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



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

    Кстати, дословный перевод 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 ver\r\n");
        sendCommand("sys get hweui\r\n");
    
        sendCommand("mac pause\r\n");
        sendCommand("radio set mod lora\r\n");
        sendCommand("radio set pwr -3\r\n"); // the transceiver output power, from -3 to 15
        sendCommand("radio set sf sf8\r\n"); // sf7..sf12, sf7 the fastest spreading factor but gives the shortest range
        // sendCommand("mac set dr 0\r\n"); // data rate: 0-4, 4 faster
        sendCommand("radio set freq 869100000\r\n");
        // sendCommand("radio set afcbw 41.7\r\n");
        sendCommand("radio set rxbw 125\r\n");
        // sendCommand("radio set prlen 8\r\n");
        sendCommand("radio set crc on\r\n");
        // sendCommand("radio set iqi off\r\n");
        sendCommand("radio set cr 4/8\r\n");
        // sendCommand("radio set wdt 60000\r\n"); // disable for continuous reception
        // sendCommand("radio set sync 12\r\n");
        sendCommand("radio set bw 125\r\n");
    }
    
    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 1234\r\n");
        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 соответственно). «К сожалению» потому, что в GNU Radio не получится иметь оба компонента сразу, инсталлятор одного компонента затирает файлы другого.

    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 появляются дополнительные блоки, из которых несложно собрать декодер. В качестве параметров декодера необходимо указать ширину полосы передачи, spreading factor, центральную частоту SDR и частоту приема.



    Блоки в 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
    

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



    Как можно видеть, в строке есть блоки заголовка и окончания передачи, а в середине мы видим наши данные «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 поверх помещается еще один логический уровень — с шифрованием, ключами и прочими сервисами. Желающие посмотреть как устроено кодирование, могут попробовать онлайн-декодер здесь.

    Кстати, из этого следует, что даже если мы получим сигнал LoRaWAN при помощи SDR, без наличия ключей шифрования (один из которых хранится на сервере провайдера), содержимого пакета мы все равно не узнаем.

    Заключение


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

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

    receive1.grc
    <?xml version='1.0' encoding='utf-8'?>
    <?grc format='1' created='3.7.11'?>
    <flow_graph>
      <timestamp>Mon Jun  3 09:39:45 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>wx_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>(760, 12)</value>
        </param>
        <param>
          <key>_rotation</key>
          <value>0</value>
        </param>
        <param>
          <key>id</key>
          <value>bw</value>
        </param>
        <param>
          <key>value</key>
          <value>125000.0</value>
        </param>
      </block>
      <block>
        <key>variable</key>
        <param>
          <key>comment</key>
          <value></value>
        </param>
        <param>
          <key>_enabled</key>
          <value>1</value>
        </param>
        <param>
          <key>_coordinate</key>
          <value>(936, 12)</value>
        </param>
        <param>
          <key>_rotation</key>
          <value>0</value>
        </param>
        <param>
          <key>id</key>
          <value>code_rate</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>1</value>
        </param>
        <param>
          <key>_coordinate</key>
          <value>(848, 12)</value>
        </param>
        <param>
          <key>_rotation</key>
          <value>0</value>
        </param>
        <param>
          <key>id</key>
          <value>header</value>
        </param>
        <param>
          <key>value</key>
          <value>True</value>
        </param>
      </block>
      <block>
        <key>variable</key>
        <param>
          <key>comment</key>
          <value></value>
        </param>
        <param>
          <key>_enabled</key>
          <value>1</value>
        </param>
        <param>
          <key>_coordinate</key>
          <value>(680, 12)</value>
        </param>
        <param>
          <key>_rotation</key>
          <value>0</value>
        </param>
        <param>
          <key>id</key>
          <value>ldr</value>
        </param>
        <param>
          <key>value</key>
          <value>True</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>(400, 12)</value>
        </param>
        <param>
          <key>_rotation</key>
          <value>0</value>
        </param>
        <param>
          <key>id</key>
          <value>offset</value>
        </param>
        <param>
          <key>value</key>
          <value>-100000.0</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>(8, 76)</value>
        </param>
        <param>
          <key>_rotation</key>
          <value>0</value>
        </param>
        <param>
          <key>id</key>
          <value>samp_rate</value>
        </param>
        <param>
          <key>value</key>
          <value>1000000</value>
        </param>
      </block>
      <block>
        <key>variable</key>
        <param>
          <key>comment</key>
          <value></value>
        </param>
        <param>
          <key>_enabled</key>
          <value>1</value>
        </param>
        <param>
          <key>_coordinate</key>
          <value>(544, 12)</value>
        </param>
        <param>
          <key>_rotation</key>
          <value>0</value>
        </param>
        <param>
          <key>id</key>
          <value>spreading_factor</value>
        </param>
        <param>
          <key>value</key>
          <value>8</value>
        </param>
      </block>
      <block>
        <key>blocks_rotator_cc</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>(416, 252)</value>
        </param>
        <param>
          <key>_rotation</key>
          <value>0</value>
        </param>
        <param>
          <key>id</key>
          <value>blocks_rotator_cc_0</value>
        </param>
        <param>
          <key>maxoutbuf</key>
          <value>0</value>
        </param>
        <param>
          <key>minoutbuf</key>
          <value>0</value>
        </param>
        <param>
          <key>phase_inc</key>
          <value>(2 * math.pi * offset) / samp_rate</value>
        </param>
      </block>
      <block>
        <key>blocks_socket_pdu</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>(944, 452)</value>
        </param>
        <param>
          <key>_rotation</key>
          <value>0</value>
        </param>
        <param>
          <key>host</key>
          <value>127.0.0.1</value>
        </param>
        <param>
          <key>id</key>
          <value>blocks_socket_pdu_0</value>
        </param>
        <param>
          <key>mtu</key>
          <value>10000</value>
        </param>
        <param>
          <key>maxoutbuf</key>
          <value>0</value>
        </param>
        <param>
          <key>minoutbuf</key>
          <value>0</value>
        </param>
        <param>
          <key>port</key>
          <value>40868</value>
        </param>
        <param>
          <key>tcp_no_delay</key>
          <value>False</value>
        </param>
        <param>
          <key>type</key>
          <value>"UDP_CLIENT"</value>
        </param>
      </block>
      <block>
        <key>import</key>
        <param>
          <key>alias</key>
          <value></value>
        </param>
        <param>
          <key>comment</key>
          <value></value>
        </param>
        <param>
          <key>_enabled</key>
          <value>True</value>
        </param>
        <param>
          <key>_coordinate</key>
          <value>(296, 12)</value>
        </param>
        <param>
          <key>_rotation</key>
          <value>0</value>
        </param>
        <param>
          <key>id</key>
          <value>import_0</value>
        </param>
        <param>
          <key>import</key>
          <value>import math</value>
        </param>
      </block>
      <block>
        <key>lora_decode</key>
        <param>
          <key>alias</key>
          <value></value>
        </param>
        <param>
          <key>code_rate</key>
          <value>code_rate</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>header</key>
          <value>header</value>
        </param>
        <param>
          <key>_coordinate</key>
          <value>(648, 452)</value>
        </param>
        <param>
          <key>_rotation</key>
          <value>0</value>
        </param>
        <param>
          <key>id</key>
          <value>lora_decode_0</value>
        </param>
        <param>
          <key>low_data_rate</key>
          <value>ldr</value>
        </param>
        <param>
          <key>maxoutbuf</key>
          <value>0</value>
        </param>
        <param>
          <key>minoutbuf</key>
          <value>0</value>
        </param>
        <param>
          <key>spreading_factor</key>
          <value>spreading_factor</value>
        </param>
      </block>
      <block>
        <key>lora_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>_enabled</key>
          <value>1</value>
        </param>
        <param>
          <key>fft_factor</key>
          <value>2</value>
        </param>
        <param>
          <key>beta</key>
          <value>25.0</value>
        </param>
        <param>
          <key>_coordinate</key>
          <value>(384, 452)</value>
        </param>
        <param>
          <key>_rotation</key>
          <value>0</value>
        </param>
        <param>
          <key>id</key>
          <value>lora_demod_0</value>
        </param>
        <param>
          <key>low_data_rate</key>
          <value>ldr</value>
        </param>
        <param>
          <key>maxoutbuf</key>
          <value>0</value>
        </param>
        <param>
          <key>minoutbuf</key>
          <value>0</value>
        </param>
        <param>
          <key>spreading_factor</key>
          <value>spreading_factor</value>
        </param>
      </block>
      <block>
        <key>pfb_arb_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>_enabled</key>
          <value>1</value>
        </param>
        <param>
          <key>_coordinate</key>
          <value>(656, 292)</value>
        </param>
        <param>
          <key>_rotation</key>
          <value>0</value>
        </param>
        <param>
          <key>id</key>
          <value>pfb_arb_resampler_xxx_0</value>
        </param>
        <param>
          <key>maxoutbuf</key>
          <value>0</value>
        </param>
        <param>
          <key>minoutbuf</key>
          <value>0</value>
        </param>
        <param>
          <key>nfilts</key>
          <value>32</value>
        </param>
        <param>
          <key>rrate</key>
          <value>bw/samp_rate</value>
        </param>
        <param>
          <key>samp_delay</key>
          <value>0</value>
        </param>
        <param>
          <key>atten</key>
          <value>100</value>
        </param>
        <param>
          <key>taps</key>
          <value></value>
        </param>
        <param>
          <key>type</key>
          <value>ccf</value>
        </param>
      </block>
      <block>
        <key>sdrplay_rsp2_source</key>
        <param>
          <key>agc_enabled</key>
          <value>False</value>
        </param>
        <param>
          <key>antenna</key>
          <value>'A'</value>
        </param>
        <param>
          <key>bw</key>
          <value>400</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>dc_offset_mode</key>
          <value>True</value>
        </param>
        <param>
          <key>debug_enabled</key>
          <value>False</value>
        </param>
        <param>
          <key>device_serial</key>
          <value>'0'</value>
        </param>
        <param>
          <key>_enabled</key>
          <value>True</value>
        </param>
        <param>
          <key>_coordinate</key>
          <value>(144, 196)</value>
        </param>
        <param>
          <key>_rotation</key>
          <value>0</value>
        </param>
        <param>
          <key>id</key>
          <value>sdrplay_rsp2_source_0</value>
        </param>
        <param>
          <key>if_atten_db</key>
          <value>30</value>
        </param>
        <param>
          <key>ifType</key>
          <value>0</value>
        </param>
        <param>
          <key>iq_balance_mode</key>
          <value>True</value>
        </param>
        <param>
          <key>lna_atten_step</key>
          <value>3</value>
        </param>
        <param>
          <key>lo_mode</key>
          <value>1</value>
        </param>
        <param>
          <key>maxoutbuf</key>
          <value>0</value>
        </param>
        <param>
          <key>minoutbuf</key>
          <value>0</value>
        </param>
        <param>
          <key>rf_freq</key>
          <value>869.0e6</value>
        </param>
        <param>
          <key>sample_rate</key>
          <value>samp_rate</value>
        </param>
      </block>
      <block>
        <key>wxgui_fftsink2</key>
        <param>
          <key>avg_alpha</key>
          <value>0</value>
        </param>
        <param>
          <key>average</key>
          <value>False</value>
        </param>
        <param>
          <key>baseband_freq</key>
          <value>0</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>fft_size</key>
          <value>1024</value>
        </param>
        <param>
          <key>freqvar</key>
          <value>None</value>
        </param>
        <param>
          <key>_coordinate</key>
          <value>(1000, 116)</value>
        </param>
        <param>
          <key>_rotation</key>
          <value>0</value>
        </param>
        <param>
          <key>grid_pos</key>
          <value></value>
        </param>
        <param>
          <key>id</key>
          <value>wxgui_fftsink2_0</value>
        </param>
        <param>
          <key>notebook</key>
          <value></value>
        </param>
        <param>
          <key>peak_hold</key>
          <value>False</value>
        </param>
        <param>
          <key>ref_level</key>
          <value>0</value>
        </param>
        <param>
          <key>ref_scale</key>
          <value>2.0</value>
        </param>
        <param>
          <key>fft_rate</key>
          <value>15</value>
        </param>
        <param>
          <key>samp_rate</key>
          <value>samp_rate</value>
        </param>
        <param>
          <key>title</key>
          <value>FFT Plot</value>
        </param>
        <param>
          <key>type</key>
          <value>complex</value>
        </param>
        <param>
          <key>win_size</key>
          <value></value>
        </param>
        <param>
          <key>win</key>
          <value>None</value>
        </param>
        <param>
          <key>y_divs</key>
          <value>10</value>
        </param>
        <param>
          <key>y_per_div</key>
          <value>10</value>
        </param>
      </block>
      <connection>
        <source_block_id>blocks_rotator_cc_0</source_block_id>
        <sink_block_id>pfb_arb_resampler_xxx_0</sink_block_id>
        <source_key>0</source_key>
        <sink_key>0</sink_key>
      </connection>
      <connection>
        <source_block_id>blocks_rotator_cc_0</source_block_id>
        <sink_block_id>wxgui_fftsink2_0</sink_block_id>
        <source_key>0</source_key>
        <sink_key>0</sink_key>
      </connection>
      <connection>
        <source_block_id>lora_decode_0</source_block_id>
        <sink_block_id>blocks_socket_pdu_0</sink_block_id>
        <source_key>out</source_key>
        <sink_key>pdus</sink_key>
      </connection>
      <connection>
        <source_block_id>lora_demod_0</source_block_id>
        <sink_block_id>lora_decode_0</sink_block_id>
        <source_key>out</source_key>
        <sink_key>in</sink_key>
      </connection>
      <connection>
        <source_block_id>pfb_arb_resampler_xxx_0</source_block_id>
        <sink_block_id>lora_demod_0</sink_block_id>
        <source_key>0</source_key>
        <sink_key>0</sink_key>
      </connection>
      <connection>
        <source_block_id>sdrplay_rsp2_source_0</source_block_id>
        <sink_block_id>blocks_rotator_cc_0</sink_block_id>
        <source_key>0</source_key>
        <sink_key>0</sink_key>
      </connection>
    </flow_graph>
    


    receive2.grc
    <?xml version='1.0' encoding='utf-8'?>
    <?grc format='1' created='3.7.11'?>
    <flow_graph>
      <timestamp>Mon Jun  3 09:39:45 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>wx_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>(184, 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>1000000</value>
        </param>
      </block>
      <block>
        <key>lora_lora_receiver</key>
        <param>
          <key>bandwidth</key>
          <value>125000</value>
        </param>
        <param>
          <key>alias</key>
          <value></value>
        </param>
        <param>
          <key>crc</key>
          <value>True</value>
        </param>
        <param>
          <key>center_freq</key>
          <value>869e6</value>
        </param>
        <param>
          <key>channel_list</key>
          <value>[869.1e6]</value>
        </param>
        <param>
          <key>cr</key>
          <value>4</value>
        </param>
        <param>
          <key>comment</key>
          <value></value>
        </param>
        <param>
          <key>conj</key>
          <value>False</value>
        </param>
        <param>
          <key>affinity</key>
          <value></value>
        </param>
        <param>
          <key>decimation</key>
          <value>1</value>
        </param>
        <param>
          <key>disable_channelization</key>
          <value>False</value>
        </param>
        <param>
          <key>disable_drift_correction</key>
          <value>False</value>
        </param>
        <param>
          <key>_enabled</key>
          <value>True</value>
        </param>
        <param>
          <key>_coordinate</key>
          <value>(456, 332)</value>
        </param>
        <param>
          <key>_rotation</key>
          <value>0</value>
        </param>
        <param>
          <key>id</key>
          <value>lora_lora_receiver_0</value>
        </param>
        <param>
          <key>implicit</key>
          <value>False</value>
        </param>
        <param>
          <key>maxoutbuf</key>
          <value>0</value>
        </param>
        <param>
          <key>minoutbuf</key>
          <value>0</value>
        </param>
        <param>
          <key>reduced_rate</key>
          <value>False</value>
        </param>
        <param>
          <key>samp_rate</key>
          <value>1e6</value>
        </param>
        <param>
          <key>sf</key>
          <value>8</value>
        </param>
      </block>
      <block>
        <key>lora_message_socket_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>_enabled</key>
          <value>True</value>
        </param>
        <param>
          <key>_coordinate</key>
          <value>(696, 364)</value>
        </param>
        <param>
          <key>_rotation</key>
          <value>0</value>
        </param>
        <param>
          <key>id</key>
          <value>lora_message_socket_sink_0</value>
        </param>
        <param>
          <key>ip</key>
          <value>127.0.0.1</value>
        </param>
        <param>
          <key>layer</key>
          <value>1</value>
        </param>
        <param>
          <key>port</key>
          <value>40868</value>
        </param>
      </block>
      <block>
        <key>sdrplay_rsp2_source</key>
        <param>
          <key>agc_enabled</key>
          <value>False</value>
        </param>
        <param>
          <key>antenna</key>
          <value>'A'</value>
        </param>
        <param>
          <key>bw</key>
          <value>400</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>dc_offset_mode</key>
          <value>True</value>
        </param>
        <param>
          <key>debug_enabled</key>
          <value>False</value>
        </param>
        <param>
          <key>device_serial</key>
          <value>'0'</value>
        </param>
        <param>
          <key>_enabled</key>
          <value>True</value>
        </param>
        <param>
          <key>_coordinate</key>
          <value>(72, 148)</value>
        </param>
        <param>
          <key>_rotation</key>
          <value>0</value>
        </param>
        <param>
          <key>id</key>
          <value>sdrplay_rsp2_source_0</value>
        </param>
        <param>
          <key>if_atten_db</key>
          <value>30</value>
        </param>
        <param>
          <key>ifType</key>
          <value>0</value>
        </param>
        <param>
          <key>iq_balance_mode</key>
          <value>True</value>
        </param>
        <param>
          <key>lna_atten_step</key>
          <value>3</value>
        </param>
        <param>
          <key>lo_mode</key>
          <value>1</value>
        </param>
        <param>
          <key>maxoutbuf</key>
          <value>0</value>
        </param>
        <param>
          <key>minoutbuf</key>
          <value>0</value>
        </param>
        <param>
          <key>rf_freq</key>
          <value>869.0e6</value>
        </param>
        <param>
          <key>sample_rate</key>
          <value>samp_rate</value>
        </param>
      </block>
      <block>
        <key>wxgui_fftsink2</key>
        <param>
          <key>avg_alpha</key>
          <value>0</value>
        </param>
        <param>
          <key>average</key>
          <value>True</value>
        </param>
        <param>
          <key>baseband_freq</key>
          <value>0</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>fft_size</key>
          <value>1024</value>
        </param>
        <param>
          <key>freqvar</key>
          <value>None</value>
        </param>
        <param>
          <key>_coordinate</key>
          <value>(688, 108)</value>
        </param>
        <param>
          <key>_rotation</key>
          <value>0</value>
        </param>
        <param>
          <key>grid_pos</key>
          <value></value>
        </param>
        <param>
          <key>id</key>
          <value>wxgui_fftsink2_0</value>
        </param>
        <param>
          <key>notebook</key>
          <value></value>
        </param>
        <param>
          <key>peak_hold</key>
          <value>True</value>
        </param>
        <param>
          <key>ref_level</key>
          <value>0</value>
        </param>
        <param>
          <key>ref_scale</key>
          <value>2.0</value>
        </param>
        <param>
          <key>fft_rate</key>
          <value>15</value>
        </param>
        <param>
          <key>samp_rate</key>
          <value>samp_rate</value>
        </param>
        <param>
          <key>title</key>
          <value>FFT Plot</value>
        </param>
        <param>
          <key>type</key>
          <value>complex</value>
        </param>
        <param>
          <key>win_size</key>
          <value></value>
        </param>
        <param>
          <key>win</key>
          <value>None</value>
        </param>
        <param>
          <key>y_divs</key>
          <value>10</value>
        </param>
        <param>
          <key>y_per_div</key>
          <value>10</value>
        </param>
      </block>
      <connection>
        <source_block_id>lora_lora_receiver_0</source_block_id>
        <sink_block_id>lora_message_socket_sink_0</sink_block_id>
        <source_key>frames</source_key>
        <sink_key>in</sink_key>
      </connection>
      <connection>
        <source_block_id>sdrplay_rsp2_source_0</source_block_id>
        <sink_block_id>lora_lora_receiver_0</sink_block_id>
        <source_key>0</source_key>
        <sink_key>0</sink_key>
      </connection>
      <connection>
        <source_block_id>sdrplay_rsp2_source_0</source_block_id>
        <sink_block_id>wxgui_fftsink2_0</sink_block_id>
        <source_key>0</source_key>
        <sink_key>0</sink_key>
      </connection>
    </flow_graph>
    


    Всем удачных экспериментов.
    Поделиться публикацией

    Комментарии 31

      0
      А можно декодер(ну в смысле весь приём) сразу на python написать?
      Или обязательно сишный модуль?
        +2
        Да, GNU Radio позволяет делать блоки на Python.

        Если интерес аудитории к GNU Radio не иссякнет, можно будет рассмотреть создание блоков отдельной темой.
          +2
          Это было бы интереснее чем
          make && sudo make install && sudo ldconfig
            +1
            >> sudo make install && sudo ldconfig

            Для тех, кому это просто, могу дать задачу посложнее — попробуйте это собрать под Windows :)

            У меня не получилось, вылезла туча всяких зависимостей.
              +1
              У меня не получилось

              Вот поэтому и лучше на python такие вещи делать (ведь понятно что это не продакшн). Чтобы как минимум со сборкой проблем не было…

              Да и проще разобраться другим…

              Если интерес аудитории к GNU Radio не иссякнет

              Интерес к этой теме думаю ни когда не иссякнет…

              П.С. Создание модулей на python было бы я думаю интересно рассмотреть. Особенно применительно к той же LoRa и прочим.
                0
                Вот поэтому и лучше на python такие вещи делать (ведь понятно что это не продакшн). Чтобы как минимум со сборкой проблем не было

                Библиотека gr-lora не моя, я только разместил объяву ;)
                0
                В одном из трех случаев получилось таки запустить это под Windows, времени было потрачено уйма.
                В двух остальных случаях сдавался и скатывался обратно на линукс.
            0
            Фишка в том, что по идеологии GNURadio именно для обрабокти сигналов используются больше С/C++ -модули(часто используя библиотеку Volk, написанную этими же разработчиками), которые при помощи SWIG оборачиваются в python модули, которые, в свою очередь, соединяются в единый workflow. Вы, конечно, можете написать модули и на python, используя, например, numpy, который в общем-то использует тот же C. Чаще всего, модули в GNURadio написанные на python — это композиция более мелких функций или блоков, носящая название Hierarchical blocks, которые, вы можете, кстати, сделать и при помощи интерфейса GNURadio.
            +4
            Чем дальше, тем интереснее)))
              +3
              О!
              Только сегодня вспоминал об Sdr-теме на хабре)
              Прям какой-то всплеск интереса к SDR. На хакере сегодня статья по SDR вышла…
              Там узнал то чего раньше не знал)
              Проект KerberosSDR.
              4 синхронизированных приёмника rtl-sdr на одной плате.
              Это даёт возможность делать очень интересные штуки типа отслеживание направления на источник сигнала и пассивный радар!
              www.rtl-sdr.com/tag/kerberossdr
              kerberossdr.com
                0
                А эта плата уже продается, или это только прототип? Ссылка ведет на Indiegogo, как я понимаю, это платформа для краудфандинга?
                  0

                  Да, тема фазированных решеток на SDR, программного определения фазы между приемниками и т.п. была бы любопытна.

                  0

                  Статья интересная, но я бы сказал что код в черновом варианте Вообще не претендует на production.

                    0

                    На али продаются модули Lora, давненько уже к ним приглядываюсь. Интересен такой аспект: Можно ли их использовать в качестве "прозрачного" удлинителя com-порта? То есть я могу одноразово прицепить Lora к компу и загнать туда какие-то байты конфигурации. А дальше надо чтобы при включении модули входили в связь друг с другом, и тупо удлиняли com-порт по радиоканалу. Главное — больше не требуя никакой инициализации. Мне надо удлинить com-порт между парой промышленных железок.

                      +1
                      Брал вот такую штуку: E32-433T30D, там кроме самого модема SX1278 стоит процессор (похоже на STM8) и усилитель до 30dbm. Есть вариант E32-433T20DT без усилителя, чуть подешевле, дальность будет поменьше. С помощью утилиты конфигурируются скорость по воздуху и COM-порту, адрес, канал. После это может в прозрачном режиме работать. Добавить только TTL-RS232 конвертер и готово. Если железки лояльно отнесутся к задержкам прохождения пакетов, то должно работать.
                        0
                        А Lora использовать обязательно? В сети масса вариантов, например с Bluetooth.
                        www.shortlink.technoton.by/short_link.htm
                          0
                          Ну, Bluetooth метров 10, LoRa до 20 км. Под каждую задачу свой инструмент.
                          0
                          Можно ли их использовать в качестве «прозрачного» удлинителя com-порта?

                          Насчет «прозрачного» не уверен, а так, RN2483 как раз и работает по serial. Если конечно скорость 300бит/с и duty cycle 1% устроит.
                            0
                            Как раз у нас в России даже ограничения на рабочий цикл нету при 25 мВт на одно из поддиапазонов. Так что вполне. На 20 км. только не рассчитывайте. Может, будет, может, нет.
                            0
                            Данный вид сигнала называется вовсе не «чирп», а вполне себе обыденно «ЛЧМ» ( линейно-частотная модуляция" — частный случай широкополосных сигналов (ШПС). Изучают ЛЧМ на радиотехнических факультетах. Хороша она тем, что в качестве согласованного фильтра может использоваться дешевая и надежная аналоговая линия задержки с частотной дисперсией. Ну, в общем, вам сюда studopedia.su/5_4382_soglasovannaya-filtratsiya-lchm-signalov.html
                              0
                              chirp это зарубежное название.
                                –1
                                chirp это аббревиатура.
                              0
                              «линейно-частотная модуляция» — частный случай широкополосных сигналов (ШПС)

                              Спасибо, название добавил в текст.

                              В англоязычных источниках действительно используется chirp, да и я эту тему изучал не на русском :)
                              +1
                              Интересно. А с год назад искал информацию про декодирование лоры, но наткнулся только на посты где говорили, что это невозможно, так как протокол закрыт.
                              А про декодирование DMR в gnuradio уже спрашивали?
                                0
                                Я думаю, эти посты писали как раз те, кто попробовали библиотеку от BastilleResearch, получили непонятные данные, и на этом закончили :)
                                  0
                                  А на практике кроме голоса в DMR передается что-то?
                                    0
                                    Я купил плагин dsd+, хотя он и бесплатно есть. Там передаётся ещё куча служебной информации, всякие id, кто и к кому или к какой группе подключился и передал сообщение, время сеанса связи, длительность. Ещё, вроде, сигналы GPS передаются, если рация поддерживает, а также и текстовые сообщения.
                                  +1
                                  Очень хорошо про ЛоРу (с матаном, но наглядно и доступно) написано в Технологии связи
                                  и еще доступна ВКР, автор Амосов Д.А., там есть исходные коды ЦОС.
                                    0
                                    Не соглашусь.

                                    1) В ВКР Амосова Д.А. рассматривается случай с постоянной начальной фазой чирпа. В реальности начальная фаза чирпа на приемнике будет произвольной (условно будет зависеть от расстояния между приемником и передатчиком и длины волны несущей частоты).

                                    2) В «Технологии связи» написано не наглядно и не доступно. Задачу можно решить различными способами, да там и написано возле рис.13 «Возможная схема приемника сигнала LoRa». Их решение в частотной области затратно и не применяется в ис Semtech и модулях nanoloc.

                                    3) Когда речь идет про chirp spread spectrum, и в частности когда есть полоса и в ней гоним up/down чирп с интерпретацией в биты 1/0 или 0/1, то я представляю себе nanoloc, а не ЛоРу. Посмотрите на начальную картинку этой статьи или на рис.12 «Технологии связи». Видите резкие скачки частоты?(Понятно что преамбула и разделитель их не содержат) Это не артефакты. Это иной способ представления передаваемых данных, не как в nanoloc. И как я понимаю — сколько скачков может быть и каким передаваемым битам это соответствует, Semtech не говорит. Но возможно самый медленный и дальнобойный режим ЛоРа оперирует чирпами во всю ширину полосы, без скачков.
                                    0
                                    Пробовал ваттную лору, максимально на прямой (почти) 12 км, в городе 3-5км. Нормального декодеоа-кодера еще не встречал, всё что в гнурадио — всё полуфабрикаты!

                                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                    Самое читаемое