Создание простого Python блока для GnuRadio

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

Для начала  — общая информация. Блоки в GnuRadio могут быть написаны на C++ или Python. C++ — универсальный выбор, с его помощью вы можете создавать как блоки для ЦОС, так и вспомогательные блоки. Блоки написанные на Python подходят только для вспомогательных блоков, так как их производительность гораздо ниже чем аналогичных блоков на C++.
В нашм случае, создания служебного модуля, Python будет подходящим выбором (а так же по той причине, что автор этих строк не может С++ :) )

Итак, какие же блоки могут быть, согласно документации.

Синхронный блок

Выбор данного типа создает блок, который получает и выдает одинаковое количество объектов или семплов на каждый порт. Синхронный блок может иметь любое число входов или выходов.
Синхронный блок не имеющий входов называется источником («Source»), а блок не имеющий выходных портов — выходом («Sink»).

Прореживающий (децимирующий) блок

В прореживающем блоке поток данных фиксированной скорости всегда имеет количество входных семплов кратное фиксированному значению количества выходных семплов.
Пример прореживающего блока на C++

Как можем увидеть конструктор четвертым параметром принимает значение децимации
Так же логично предположить что количество выходных семплов равно количеству выходных деленному на коэффициент децимации.

Интерполирующий блок

Интерполирующий блок так же поток данных фиксированной скорости и имеет количество входных семплов кратное фиксированному значению количества выходных семплов.
Пример интерполирущего блока на C++

Количество выходных семплов равно количеству выходных умноженному на коэффициент интерполяции.

 Базовый блок

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

Вот пример базового блока:

Пару замечаний по поводу базового блока:

— Класс перезагружает метод general_work(), а не work()
-У general_work() есть параметр ninput_items. ninput_items явлется вектором описывающим размер буфера для каждого входного порта.
— Перед возвратом значения из general_work() нужно потребить (consume) использованные входы.
— Предполагается что количество элементов во входных буферах равно noutput_items. Пользователь может изменить это поведение перезагрузкой метода forecast()

Таковы 3 базовых типа блоков в GnuRadio. И судя по этой информации нам потребутеся базовый блок. Ну что же, давайте займемся непосредсвенно созданием блока.

GnuRadio имеет в своем составе утилиту для генерации скелета нового блока gr_modtool.  Откроем консоль, перейдем в каталог в котором будем работать, а затем сгенерируем скелет блока который мы назовем «srig» следующей командой:

$ gr_modtool newmod srig
Creating out-of-tree module in ./gr-srig… Done.
Use ‘gr_modtool add’ to add a new block to this currently empty module.

Утилита создала каталог с именем «gr-srig». Переместимся в него, и инициализируем служебные файлы для блока котрый будет написан на Python

$ cd gr-srig
$ gr_modtool add -t general -l python
GNU Radio module name identified: srig
Language: Python
Enter name of block/code (without module name prefix): srig_py
Block/code identifier: srig_py
Enter valid argument list, including default arguments: serialPort, pin0, pin1, pin2, pin3
Add Python QA code? [Y/n] n
Adding file ‘python/srig_py.py’…
Adding file ‘grc/srig_srig_py.xml’…
Editing grc/CMakeLists.txt…

Утилит спросит нас имя для файла в котором будет код (srig_py), параметры которые должны быть переданы при инициализации (у нас это будет имя последовательного порта, и состояния 4 выходов устройства), а так же требуется ли добавлять код для Unit тестирования и запускать его (в нашем случае мы можем не делать этого, но в случае серьезного проекта это настоятельно рекомендуется).

Теперь переместимся в каталог «python» и и откроем сгенерированный файл srig_py.py

Скелет состоит из 3 методов: __init__() для первичной инициализации, forecast() для входных буферов, и general_work() в котором и должна происходить обрабаботка сигналов. В нашем случае блок не будет обрабатывать никаких сигналов, а будет лишь менять состояние внутренних переменных по запросу, и при изменении состояиния переменных- отдавать команду внешнему устройству через последовательный порт.
Теперь я модифицирую скелет блока, и дам пояснения.

строка 4: Импортируем модуль для работы с последовательным портом.
строки 14-15:  Здесь я просто раскомментировал указания, данные какого типа должны быть на входах и выходах. Для нас это не имеет смысла, така как блок не обрабатывает данные.
строка 16: создаем прееменную типа «словарь», дабы сопоставить значение True и False с ASCII символами «1» и «0». Пригодится для отправки команды устройству.
строка 17: инициализируем объект для работы с последоватльным портом.
строки 18-24: Открываем порт.
строки 25-28: создаем 4 внутренних переменных для хранения состояния выхода устройства, и присвояем им первичные значения полученные при иницилизации.
строки 34-57: Здесь я написал 4 практически одинаковых сеттера для проверки значения и передачи команды устройству. Суть такова, что если при работе графа будет изменена какая-нибудь переменная этого блока, то это вызовет метод «set_имяпеременной(новое_значение)«. Следовательно создав методы set_pin0, set_pin1 и т.д. мы можем поймать новое значение переменной. Далее мы сравниваем новое значение с текущим, и если они отличаются, сохраняем новое, и отправляем команду устройству через последовательный порт. Формат команды незатейлив:»Sномер_портаPсостояние_порта»

С кодом все :). Теперь создадим графическое представление для нашего блока, которое мы будем добавлять на схему. Сохраняем файл, перемещаемся из каталога python выше, и заходим в каталог grc. В нем мы открываем файл srig_srig_py.xml и обновляем его как показано ниже.

строка 3: Задаем отображаемое на блоке название
строка 5: Категория в которой находится блок. Если она не существует, то будет создана.  В нашем случае это будет выглядеть вот так:
in_listстроки 8-11: тут мы и указываем, что при изменении переменных «pinN» нужно вызывать метод set_pinN().
строки 17-41: Описываем какие поля будут отображаться в блоке когда мы его откроем, их описания. Все довольно ясно.

Сохраняем xml файл описания блока. Осталось совсем немного, нужно лишь немного подправить место, куда будут скопированны файлы блока. В том же каталоге grc открываем файл CMakeLists.txt  и изменяем строку

srig_srig_py.xml DESTINATION share/gnuradio/grc/blocks
на
srig_srig_py.xml DESTINATION /usr/share/gnuradio/grc/blocks

Все готово для установки и проверки. Перемещаемся на уровень выше в каталог gr-srig, и выполняем стандартную процедуру сборки:
$ mkdir build
$ cd build/
$ cmake ../
— The CXX compiler identification is GNU 5.4.0
— The C compiler identification is GNU 5.4.0
— Check for working CXX compiler: /usr/bin/c++
— Check for working CXX compiler: /usr/bin/c++ — works
— Detecting CXX compiler ABI info
— Detecting CXX compiler ABI info — done
— Detecting CXX compile features
— Detecting CXX compile features — done
— Check for working C compiler: /usr/bin/cc
— Check for working C compiler: /usr/bin/cc — works
— Detecting C compiler ABI info
— Detecting C compiler ABI info — done
— Detecting C compile features
— Detecting C compile features — done
— Build type not specified: defaulting to release.
— Boost version: 1.58.0
— Found the following Boost libraries:
— filesystem
— system
— Found PkgConfig: /usr/bin/pkg-config (found version «0.29.1»)
— Checking for module ‘cppunit’
— Found cppunit, version 1.13.2
— Found CPPUNIT: /usr/lib/x86_64-linux-gnu/libcppunit.so;dl
— Could NOT find Doxygen (missing: DOXYGEN_EXECUTABLE)
Checking for GNU Radio Module: RUNTIME
— Checking for module ‘gnuradio-runtime’
— Found gnuradio-runtime, version 3.7.9
* INCLUDES=/usr/include
* LIBS=/usr/lib/x86_64-linux-gnu/libgnuradio-runtime.so;/usr/lib/x86_64-linux-gnu/libgnuradio-pmt.so
— Found GNURADIO_RUNTIME: /usr/lib/x86_64-linux-gnu/libgnuradio-runtime.so;/usr/lib/x86_64-linux-gnu/libgnuradio-pmt.so
GNURADIO_RUNTIME_FOUND = TRUE
— No C++ sources… skipping lib/
— No C++ sources… skipping swig/
— Found PythonInterp: /usr/bin/python2 (found suitable version «2.7.12», minimum required is «2»)
— Could NOT find Doxygen (missing: DOXYGEN_EXECUTABLE)
— Configuring done
— Generating done
— Build files have been written to: /storage/Temp/gr-srig/build

$ make
Scanning dependencies of target pygen_python_31622
[ 50%] Generating __init__.pyc, srig_py.pyc
[100%] Generating __init__.pyo, srig_py.pyo
[100%] Built target pygen_python_31622
Scanning dependencies of target pygen_apps_9a6dd
[100%] Built target pygen_apps_9a6dd

$ sudo make install
[sudo] пароль для igor:
[100%] Built target pygen_python_31622
[100%] Built target pygen_apps_9a6dd
Install the project…
— Install configuration: «Release»
— Up-to-date: /usr/local/lib/cmake/srig/srigConfig.cmake
— Up-to-date: /usr/local/include/srig/api.h
— Up-to-date: /usr/local/lib/python2.7/dist-packages/srig/__init__.py
— Up-to-date: /usr/local/lib/python2.7/dist-packages/srig/srig_py.py
— Installing: /usr/local/lib/python2.7/dist-packages/srig/__init__.pyc
— Installing: /usr/local/lib/python2.7/dist-packages/srig/srig_py.pyc
— Installing: /usr/local/lib/python2.7/dist-packages/srig/__init__.pyo
— Installing: /usr/local/lib/python2.7/dist-packages/srig/srig_py.pyo
— Up-to-date: /usr/share/gnuradio/grc/blocks/srig_srig_py.xml
$ sudo ldconfig

Все! Мы имеем новый блок в GnuRadio. Можно запустить и проверить что получилось.

srig_in_gnuradio
Давайте теперь проверим как оно работает. Я воспользуюсь своей отладочной платой с микроконтроллером Atmega8, которая будет зажигать светодиоды когда значение соответсвующего выхода будет True.

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

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">