STM32: добавляем Ethernet

В предыдущей статье я рассказывал о том, как хорошо работать с микроконтроллерами STM32 без операционной системы. Однако может наступить момент, когда вам понадобится наладить взаимодействие с вашим МК по сети. Например, что-нибудь типа умного дома или передача отладочной и диагностической информации на web страницу. Наличие Ethernet интерфейса — это конечно сильный аргумент для того, чтобы бросить STM32 и перейти на полноценный ARM процессор под управлением Linux. Однако, этот аргумент парируется очень просто: на рынке есть компактные Ethernet модули, которые можно задействовать в наших STM32 проектах. Речь идет о чипах семейства W5500 WIZnet, на основе которых эти модули и собраны. Добавляем к нашему проекту этот модуль (хвала Али Экспрессу, как обычно!), и получаем функциональность Ethernet в МК STM32.

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

 

stm32, stm32f103, Ethernet, W5500

Микроконтроллер STM32F103 и модуль Ethernet W5500 в сборе

Работать с модулем W5500 — это сущий ад. В нем куча регистров, в которые нужно заносить и считывать информацию. Когда я раскрыл мануал этого чипа, то первое что я сделал — это сразу закрыл его и начал искать библиотеку, в которой вся эта регистровая часть уже реализована и которая предоставляет такие привычные и понятные интерфейсы: socket(), listen() и так далее. И я ее нашел.

Как работать с библиотекой я расскажу дальше, а пока поработаем с нашим фото.

Пройдемся по макетке слева направо и покажем, что я там разместил.

Макетная сборка

Модуль W5500

Слева, как вы уже догадались, Ethernet модуль W5500. В этом варианте исполнения его пины смотрят вниз, что удобно для размещения на макетной плате. Есть вариант и пинами вверх, что будет удобно если вы решите просто соединять контакты напрямую кабелями. Собственно чип W5500 распаян под зеленой платкой, из-за чего создается впечатление что кроме Ethernet разъема больше ничего нет. На самом деле это не так. Чип берет на себя всю работу по поддержке сетевых протоколов, а работать с ним нужно по интерфейсу SPI. Поэтому красные и темные проводки — это и есть соединения SPI с микроконтроллером.

Кто уже работал с SPI, сейчас испытывают небольшой диссонанс. Этот протокол вообще-то говоря принято использовать для внутриплатных соединений, и выводить его наружу считается моветоном. Пришлось поступиться принципами ради демонстрационных целей, потому что нужна скорость, которую другие популярные интерфейсы — I2C и UART обеспечивать не могут. Если же вы распаяете модуль W5500 на своей собственной плате, то правила поведения в приличном обществе будут соблюдены.

UART

Смещаем взгляд правее и видим вертикально воткнутую платку. Это адаптер UART-USB, с которым мы познакомились раньше. Его мы используем исключительно для отладочных целей, для того чтобы выводить сообщения  нашей программы на экран PC.

От МК этой плате требуется только одна линия: Tx (отдаем строчки), которая на адаптере будет обозначаться уже как Rx (принимаем строчки). Ну и само собой кабель, который вы видите на торце платы, подключен к USB разъему PC.

Переключатель на плате устанавливаем в положение 3.3В: это будут уровни линий передачи данных, с которыми работает адаптер.

МК STM32F103 Blue Pill

По центру — наш микроконтроллер, плата МК STM32F103C8T6. Здесь неожиданностей нет, единственное я использовал компактную версию под названием Blue Pill. Есть еще Black Pill, Red Pill но честно говоря не знаю чем все они принципиально отличаются друг от друга. Работать будут все.

В комплекте с этими платами идут линейки пинов, которые я распаял снизу. Это позволяет вставить пины контроллера в макетную плату. Если вы используете Pill’ы в своих устройствах, запаивать пины нет необходимости — можно сразу паять соединения на ламели по краям платы. В нашей конфигурации используются контакты соответствующие UART1 и SPI2.

К разъему JTAG в торце платы МК как обычно подключен программатор/отладчик ST-LINK V2. Его мы видим сверху. Его место — также в USB разъеме PC.

USB разъем для электропитания не используется — напряжение подается непосредственно на пины МК.

Электропитание

На правой стороне макетной платы расположен весьма удобный модуль электропитания, который обеспечивает независимые напряжения 5В или 3.3 В по обеим линиям питания макетной платы (синие и красные линии). Своими пинами ложится в аккурат на эти линии, плюс к этому есть возможность подключиться еще сверху. Входное напряжение для модуля — 12В.

Модуль питания сконфигурирован джамперами на выходное напряжение 3.3В, отбор питания идет по внешним линиям модулями W5500 и МК STM32, которым требуется 3.3В. На фото видны эти желтые и коричневые короткие перемычки, которые идут рядом.

Нюанс: вы пожете подключить к МК вместо 3.3В 5В к соответствующему (другому естественно!) контакту. Эти 5В будут преобразованы в 3.3В.

Внимание! Не подключайте к плате внешнее питание 3.3В и 5В от USB одновременно. Можете потерять МК ) Также по этой причине провод +5В для JTAG от USB PC болтается в воздухе, как может заметить внимательный читатель )

Модуль UART требует электропитание 5В, которое мы обеспечиваем внешним проводком который подключается к пину +5В модуля питания. Есть подозрение, что он прекрасно будет работать и без этого проводка от USB, когда тот подключен (и работает на самом деле).

Ситуация когда к одной цепи питания модуля подключаются два источника питания — не совсем здоровая. Картинку портит поступающие 5В от USB, которые преобразуются в 3.3В и эта цепь потенциально может конфликтовать с внешней линией питания 3.3В. В таких случаях обычно ставят диод Шоттки, который защищает цепи питания 3.3В от реверсного тока. Будем надеяться, что так оно и есть )

Зачем вообще нужен модуль питания, если раньше мы прекрасно запитывали микроконтроллер от USB? Не забываем, что теперь у нас появился серьезный потребитель по напряжению 3.3В — это чип W5500, который не в лучшие для нас моменты готов принять до 185мА. Внутренний преобразователь уровня 5/3.3В микроконтроллера на такой подвиг не способен, если нам в голову придет мысль взять напряжение 3.3В оттуда.

Конфигурация

Как и раньше, для создания проекта воспользуемся услугами STM32CubeMX. По традиции создаем проект на основе Makefile, чтобы не городить огород с визуальными системами разработки (помните наш проект hardcore? Только Makefile и vim!). Как обычно, включаем пункт Debug:Serial Wire в меню SYS, чтобы иметь возможность прошивки и отладки. Сразу включатся пины PA13, PA14: через них STLINK будет общаться с МК.

Осталось сконфигурировать интерфейсы и все. Включаем USART1: Mode Asynchronous (Куб задействует пины PA9, PA10) и включаем SPI2: Mode Full-Duplex Master. На вкладке конфигурации для SPI2 нужно будет подправить параметр Prescaler: установить его в значение 4.

Почему SPI2 а не SPI1? Так было в библиотеке поддержки чипа W5500, я не стал менять интерфейс. Видимо, разработчикам было удобно работать с пинами именно с этой стороны модуля микроконтроллера ) В нашем случае Куб выдаст для SPI пины PB13 — PB15.

Нам еще понадобится управлять пином PB12 как выходным (забегая вперед — библиотека использует его для включения чипа W5500). Поэтому кликаем на него и делаем GPIO_Output. И точно также, сразу делаем выходным пин PC13: к нему подключен светодиод МК, и грех не воспользоваться возможностью поморгать светодиодом в нужных местах.

Создаем проект и переходим к следующему шагу.

Соединения

Поскольку распиновка модулей W5500 и UART-USB и так известна, а распиновку Blue Pill мы уже получили с помощью Куба, займемся соединениями. Работа приятная, медитативная, навевает мысли о тщете всего сущего, хорошо заниматься этим перед сном, глубокое погружение гарантировано 🙂

Начнем с модуля W5500. Все, что нам от него нужно — это цепи SPI и питания. С подключением питания все понятно — задействуем цепи 3.3В и GND, самое главное — правильно соединить линии интерфейса SPI. Поскольку на модуле нет нумерации пинов, на схеме соединений слева будем указывать SPI обозначение пина по версии W5500, справа — номер пина и SPI обозначение по версии Куба:

Распиновка модуля W5500


 

Памятка по SPI:

  • MISO — Master In, Slave Out: если мы в режиме мастер — принимаем данные, в режиме периферии — передаем;
  • MOSI — Master Out, Slave In: в режиме мастер передаем данные, в режиме периферии — принимаем.

В нашей конфигурации W5500 будет периферийным модулем, который будет выбираться сигналом SCNn с МК. Поэтому по линии MISO данные будут идти от W5500 к МК, по линии MOSI — в противоположном направлении. Обратите внимание, что нет необходимости переполюсовки Tx/Rx как в UART: назначение вывода (вход или выход) меняется в зависимости от того, в каком режиме — Master или Slave работает модуль.

С модулем UART-USB все просто. Нужна только одна линия передачи данных:

И в заключение — подключаем программатор/отладчик ST-LINK V2:

Цепь 3.3В остается неподключенной!

Не забудьте соединить «земли» всех модулей с землей модуля питания и подать 3.3В на модули W5500 и Blue Pill. Модуль UART-USB, как я говорил до этого, подключается к пину 5В модуля питания.

W5500 Library

Наконец наступил долгожданный момент — подключение библиотеки модуля W5500, чтобы с ним можно было работать вменяемым образом. Саму библиотеку можно скачать здесь. На самом деле, это не совсем библиотека, а набор файлов которые нужно включить в наш проект. Они точно также будут компилиться и собираться, как и наши собственные файлы. Поэтому по структуре это не библиотека, а дополнительные к нашему проекту кусочки. Но я все равно буду называть это библиотекой — по существу. Кому нравится — могут называть это также драйвером; сама WizNet вообще использует наименование Library_Driver.

Подобные продукты третьих сторон я храню отдельно от своего проекта, потому как не нужно дублировать исходники в каждом отдельном проекте и тем более включать их в систему контроля версий. Для этого у меня специальная директория — /home/user/bin, куда и скачаем библиотеку WizNet она же драйвер W5500.

Распаковываем архив и меням имя на ioLibrary_Driver. Далее, нужно настроить библиотеку на определенную версию чипа. Для этого в файле Ethernet/wizchip_conf.h ищем строчку вида

И меняем W5500 на модель чипа, которая у вас в наличии. Само собой у меня менять ничего не пришлось.

Теперь нужно как-то интегрировать исходники библиотеки в наш проект. Это делается через Makefile. Добавляем в него строки следующим образом (будьте внимательны: показаны также текущие строки для ориентации по файлу):

Все достаточно просто, не правда ли? Наступил момент запуска библиотеки. Для этого ее нужно инициализировать и выполнить тестовый пример — запустить TCP сервер который будет принимать входящее соединение и отвечать чем-то в стиле «Hello World».

Software

Как вы наверное уже догадались, все это будет жить в файле tcp.c. Но вначале библиотеку нужно инициализировать. Этот код разместим в main.c, поскольку так будет проще доступаться к интерфейсным переменным.

Первым делом нужно приготовить для библиотеки наши callback функции, которые она будет дергать когда ей понадобится доступ к SPI:

Теперь библиотеке нужно как-то сообщить о нашей проделанной работе. Регистрация наших callback-функций выполняется следующей парой строк:

Само собой, это уже вызовы функций библиотеки. Дальнейшая инициализация:

Обращаем внимание на то, что прямо здесь идут сетевые настройки модуля. В результате в структуре netInfo будет храниться вся сетевая информация, которую вы можете вывести на экран вашего компа через модуль UART-USB сразу после инициализации.

Ну а теперь можно вызывать нашу функцию tcp_server(), которая будет делать всю полезную работу. Функцию разместим в файле Src/tcp.c. Не забудем объявить ее в заголовке main.h:

Так уж и быть, выложу полное содержание tcp.c. Как все это работает, описал в комментах. Для тех, кто знаком с логикой создания входящих tcp соединений, трудностей не будет никаких.

Проверяем все это так. Соединяем патчкордом модуль W5500 и наш комп. Конфигурируем проводное соединение, у меня на PC это адрес 192.168.77.5. Пингуем модуль ping 192.168.77.6. убеждаемся что пинги проходят.

Дальше, достаем швейцарский нож хакера — программу netcat, коннектимся к модулю:

и получаем в ответ долгожданное «Hello World», а в экране minicom наблюдаем логи, которые посылает нам trace через модуль UART-USB.

Некоторые размышления по структуре программы. В ожидании входящих соединений, МК видимо будет выполнять полезную работу. Например обрабатывать поток радиолокационных данных ) Возникает проблема одновременной поддержки двух процессов. В Linux среде это решается просто — системным вызовом select(), который может зависать на нескольких событиях. В STM32 Линукса нет, но зато для STM32 есть простая операционная система реального времени FreeRtos. И она умеет работать с потоками, то есть содержит простейший планировщик заданий. Даю наводку, а как вы ей распорядитесь — ваше дело )

Про прошивку МК и отладку я подробно написал в предыдущей статье. Поэтому здесь описание этих процессов опущено.

STM32: жизнь без операционной системы

Возможно, вы очень хотели заняться чем-нибудь, что связано с чипами, прошивками, возможно даже использовать новые знания для домашних поделок — мало ли вещей, которые можно автоматизировать, начиная от контроллера солнечной батареи и заканчивая автоматом запуска бензоагрегата? При этом, не сильно погружаясь в особенности операционных систем Linux / Windows и не используя сильно избыточные платы, такие как Raspberry Pi, BeagleBoard  и подобные. Тогда добро пожаловать сюда: здесь мы запускаем микроконтролллер STM32, чистый «bare metal», никакой операционки, железо на расстоянии вытянутой руки и богатая собственная периферия.

В данной статье мы покажем то, чего не хватает в других публикациях в инете: не погружаясь в детали, схватим весь процесс разработки, начиная от простейшей программы Hello World и заканчивая ее прошивкой в контроллер и отладкой. Ну и конечно, как дань традиции, помигать светодиодом — без этого Hello World для микроконтроллеров получится совсем не солидным.

В этой статье не будет длинных листингов кода, которыми так славятся ресурсы по STM32, и не будет утомляющих скриншотов STM32CubeMX. Все будем описывать текстом — емко и кратко.

Набираем инструменты

Нам понадобятся приложения:

  • STM32CubeMX — конфигуратор;
  • текстовый редактор, например vim (категорически рекомендую освоить его);
  • ST-LINK, которое состоит из двух утилит: st-flash для прошивки STM32 и st-util для отладки, или точнее st-util это настоящий gdb сервер;
  • комплект для кросс-компиляции arm-none-eabi.

Сразу примечание: берем кросс-компилятор именно с «none», поскольку опять таки bare metal и другие варианты типа arm-linux-eabi  нам не подходят, потому как подразумевают наличие операционной системы со всем набором сопутсвующих библиотек, которые у нас будут отсутствовать напрочь.

Теперь у вас есть первая самостоятельная работа — найти и поставить эти приложения. Дальше я предполагаю, что на десктопе у вас стоит Linux, и изложение будет идти именно в этом ключе. Впрочем и для Windows процедуры будут отличаться не сильно.

Вторая самостоятельная работа — приобретение оборудования (хвала Али Экспрессу!). Нам понадобятся:

  • плата с микроконтроллером STM32F103;
  • программатор и отладчик St Link V2, похожий на флешку;
  • миниатюрная платка адаптера UART — USB.

Цена вопроса за все про все — около 500р, ждать не больше месяца.

Да, чуть не забыл: закажите еще набор проводков с окончаниями «мама»: их удобно надевать на пины плат, получится очень аккуратно.

Roadmap

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

Первым делом мы начнем использовать STM32CubeMX. Куб — это конфигуратор, который создает все файлы проекта со своей структурой каталогов. Если мы с самого начала знаем чего хотим, запустить его можно только один раз и потом про него забыть. Проект будет создан, мы будем менять код в программе, но структура останется. Еще раз — Куб это не среда разработки, а скажем так генератор шаблонов. Тем не менее сгенерированный им проект — вполне рабочий.

Сразу скажу, что Куб умеет формировать проекты для IDE разных типов. Мы, как поклонники vim и хардкора, никакими IDE пользоваться не будем — зачем нам эти бестолковые графические прослойки, заслоняющие реализацию? Редактор и Makefile — вот и все что нам нужно.

После того как структура проекта создана, мы внесем в исходники нечто похожее на вывод Hello World: должно же быть и какое-то наше участие в проекте. Сильно заморачиваться не будем, от нас потребуется буквально пара строчек. Сразу назовем наш проект сочно и звучно — hardcore.

После этого проект готов. Запускаем make и компилируем его, в результате чего создастся директория build и там появятся интересующие нас файлы: hardcore.bin — прошивка и hardcore.elf — исполняемый файл, который ценен тем что содержит отладочную информацию.

Следующим этапом прошиваем микроконтролллер:

и после этого наша программа начнет работать. Мигающий на плате светодиод мы увидим и так, а чтобы посмотреть на заветную строчку Hello World которую выдает наше приложение, на компе нужно будет запустить терминал (конечно, если вы не забыли подключить адаптер UART — USB):

Мы также можем начать отладку нашей программы — например, пройти ее пошагово, для этого запускаем st-util и потом отладчик arm-none-eabi-gdb.

Общая картина ясна, дело за подробностями.

Связи

Опишем как все это соединяется друг с другом, не забывая стыки железа и софта.

Во первых, нужно соединить программатор / отладчик St Link V2 (который я для краткости далее буду называть донглом) и плату микроконтроллера. Донглу нужно две линии — по одной, двунаправленной он будет передавать и принимать данные, по другой — выдавать сигнал синхронизации. Плюс земля и питание платы: это удобно, не нужно подключать к плате источник питания, оно будет от донгла через USB разъем компа.

В комплекте к донглу идет как раз четырехжильный кабель, которым мы и воспользуемся. Подключаться нужно к 20-пиновому JTAG разъему платы микроконтроллера. Схема подключения:

Вставляем донгл в USB, и если мы все сделали правильно, на плате загорится светодиод питания, и — надо же! начнет мигать другой светодиод — кто-то за нас позаботился о прошивке тестовой программы. Ну ничего, мы ее все равно сотрем, когда будем записывать свою.

Если вы решили запитать плату STM32 от внешнего источника питания, контакт +3.3В подсоединять не нужно. И помните золотое правило: прежде чем подавать питание все «земляные» контакты должны быть уже соединены!

Проверим наличие связи с платой:

Если выходит сообщение такого типа, что устройство обнаружено, то все в порядке.

Мы использовали последовательную линию для работы с JTAG и нужно не забыть сказать об этом потом конфигуратору Куб, иначе никаких прошивок потом не получится. Сразу говорю — если вы попали на неправильный параметр, прошили плату и сбили эту настройку, это лечится длительным нажатием Reset платы )

Во-вторых, нам нужно соединить плату STM32  и терминал в компе, чтобы посылать на терминал строчку Hello World. Для этого соединим пин PB6 платы и пин Rx адаптера UART — USB, а также свяжем их «земляные» контакты. Почему именно этот пин? Забегая вперед — именно его нам выдаст Куб когда мы будем настраивать UART. Схема соединений:

Подключаем кабелем адаптер ко второму разъему USB, Линукс увидит его как устройство /dev/ttyUSB0. К этому устройству мы и подключаем терминал, как было показано выше.

Куб

Вот так с этими микроконтроллерами — столько возни из-за двух строчек кода. Запускаем Куб, и он первым делом потребует от нас указать, с каким микроконтроллером мы работаем. Если вы не ошиблись с заказом на Ali Express, то смело указывайте STM32F103C8Tx, в противном случае берите лупу и читайте обозначение на корпусе чипа. Далее быстро вспоминаем, какие две вещи мы должны проделать с конфигуратором.

Во первых, настройка коннектора JTAG. Открываем пункт SYS и устанавливаем Debug в состояние Serial Wire: это наш последовательный интерфейс связи платы с донглом St-Link V2.

Во-вторых, настраиваем UART: открываем USART1 и ставим Mode Asynchronous. Больше ничего делать не нужно. После этой манипуляции на вкладке Configuration появится кнопка USART1, где можно менять параметры порта. Заметим, что на схеме чипа появится метка у пина PB6: он будет назначен как передающий (Tx).

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

Вот в принципе и все. Заходим в Project/Settings, даем нашему проекту имя hardcore и в выпадающем меню Toolchain / IDE выбираем Makefile, как и договаривались — никаких IDE!

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

Нажимаем на кнопку с шестеренкой, и наш проект готов: он появится в папке hardcore по тому маршруту, который вы указали в настройках.

Исходные файлы созданы, это вполне рабочий проект. Мы можем запустить make из директории hardcore, после этого исходники откомпилируются, скомпонуются и во вновь созданном каталоге build появятся исполнительные файлы hardcore.elf и hardcore.bin. Файл bin — это прошивка контроллера, файл в elf формате это исполнительный формат Linux. Конечно, исполнять его никто нигде не собирается — он нужен лишь постольку, поскольку в нем содержится отладочная информация: нумерация строк кода, имена переменных, функций и так далее. Нюанс: на самом деле собирается hardcore.elf, а потом уже из него утилитой objcopy извлекается прошивка hardcore.bin, избавляя последнюю от всего лишнего.

Та часть структуры проекта, которая представляет для нас интерес, лежит в каталогах Src/ и Inc/. Остальное трогать не нужно. Куб рассматривает созданное им дерево файлов и каталогов как свою безраздельную собственность, поскольку ему добавлять или удалять куски файлов в зависимости от того, как мы будем менять конфигурацию проекта. Он милостиво разрешает нам писать свой код в места обозначенные как /* USER CODE BEGIN */ и гарантирует, что трогать их не будет. Поэтому запускаем vim, открываем Src/main.c и ищем такой кусок в районе while(1).

Vim

Наверное несколько странно что после инициализации железа программа в main.c входит в бесконечный цикл ) А чего вы ждали, операционной системы нет, соответственно нет планировщика, и чем будет заниматься контроллер в холостом режиме — полностью наша забота. Поэтому после while(1), строго не выходя за рамки установленные нам Кубом, вставляем строчки кода таким образом:

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

Поскольку помимо помигивания светодиодом мы решили выводить настоящую текстовую строку, сделаем это командой

где 13 — длина нашей строки, подсчитанная вручную (вот ведь, всплыла чертова дюжина).

Команду можно вбить перед задержкой.

Когда выше я сказал, что можно запустить make, я несколько слукавил. Makefile для работы не хватает информации о том, где расположены все инструменты для сборки — toolchain. Обычно для этого задают переменную окружения GCC_PATH, но у меня на машине несколько разных компиляторов, поэтому я указал эту переменную в самом Makefile. Выглядит это так:

Ваши инструменты могут быть в другом месте, у меня они находятся под каталогом /opt.

Теперь запускаем make, и наша прошивка готова. Результаты работы — объектные и исполняемые файлы создаются в каталоге build.

Прошивка микроконтроллера STM32

Надеюсь, к этому моменту вы осилили соединение донгла St-Link V2 и платы микроконтроллера. Вставляем St-Link V2 в usb и начинаем прошивку.

Прошивать будем из командной строки. Если вы в папке hardcore, запускайте команду:

программатор выведет на экран результаты своей работы и если все нормально, завершит торжествующей фразой jolly good.

Признаком хорошего тона будет выполнять прошивку по команде make install. Для этого добавим в конец Makefile пару строк:

Внимание: перед st-flash должна быть табуляция а не пробелы! make весьма строго относится к этому, и если вы напутаете это может быть источником трудно распознаваемой ошибки.

Теперь процесс прошивки будет выглядеть как make install. Со временем вы можете сами дополнить Makefile, чтобы по одной этой команде также выполнялась сборка.

Очистка всего проекта с удалением каталога build выполняется соотвествующей командой make clean.

Запуск

После прошивки микроконтроллер запускается (если он притормозил, дайте пинка кнопкой reset  на плате), и наблюдайте мигающий светодиод.

Дальше, если вы не накосячили с подключением UART, после подключения адаптера вы увидите устройство /dev/ttyUSB0. Usb теперь стало стандартным последовательным устройством unix, поэтому с ним можно работать с терминальной программой. Запускаем ее:

Настройки терминала менять не нужно. Значение скорости 115200 по умолчанию подходит, также подходят установки количества бит и признака четности.

Если все сделано правильно, адаптер будет подмигивать на каждую передаваемую строку, а на экране вы увидите строчки «Hello World» каждую секунду.

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

Отладка

До сих пор поклонники IDE наблюдали за нами со сжатыми зубами, дожидаясь момента когда нам надо будет отлаживать программу. Вот тут они и выкинут свой главный козырь: а где подсветка строки, точки останова, отображение переменных? На самом деле, в реальной жизни, все это гораздо проще реализуется из командной строки. Перед глазами нет ничего лишнего, а только то на чем необходимо сконцентрировать внимание.

Перед тем как двинуться дальше, сделаем небольшой экскурс в кроссплатформенную отладку. Если вы пользовались знаменитым отладчиком gdb на своей станции, то скорее всего для вас было само собой разумеющимся то, что результаты отладки и само исполнение программы происходит на одной и той же машине. Однако, в общем случае это не так. Для нашей кроссплатформенной системы мы запускаем отладчик на своей PC Ubuntu, а программа работает на микроконтроллере STM32. Как все это должно быть состыковано вместе?

Все это уже предусмотрено в gdb. Мы даже не подозреваем о том, насколько это мощное приложение. В нашем случае мы будем использовать возможности работы отладчика в режиме клиент — сервер. Клиент — это приложение gdb на нашей машине, а сервер… занавес открывается… в качестве сервера будет выступать наш донгл St-Link V2. Не забываем о том, что помимо программатора это также gdb — сервер!

Принцип отладки выглядит следующим образом. Донгл St-Link V2 работает с платой микроконтроллера по интерфейсу JTAG и имеет возможность запускать и останавливать исполнение программы аппаратным способом. Все таки будет приятно знать, что когда мы соединяли его проводками, мы получили не только программатор но еще оказывается и отладку! С другой стороны, gdb сервер донгла запускается утилитой st-util и начинает прослушивать порт 4242 на нашей машине (значение порта по умолчанию можно поменять в командной строке). Мы со своей стороны запускаем отладчик gdb также на нашей машине, и указываем ему приконнектиться к gdb серверу донгла. С этого момента запущенный нами gdb становится клиентом, и все команды которые мы будем вводить в командной строке, будут исполняться St-Link V2.

До этого момента для краткости изложения я говорил про приложение gdb, но на самом деле конечно же мы используем не родной отладчик Ubuntu, а кроссплатформенный отладчик который идет в составе toolchain:

Общий принцип теперь ясен, переходим к деталям. Чтобы не плодить консоли, запускаем gdb сервер донгла St-Link V2 в фоновом режиме:

Запускаем в этой же консоли кроссплатформенный отладчик

или просто arm-none-eabi-gdb, если вы включили маршрут /opt/arm-none-eabi/bin в переменную окружения PATH. Отладчик выводит приглашение (gdb) и переходит в интерактивный режим.

Первым делом цепляемся к gdb-серверу донгла:

С этого момента появляется возможность задать специальные команды для сервера через ключевое слово monitor; первое что сделаем — это сбросим и остановим микроконтроллер:

Сразу замечу, что с помощью команды monitor можно также выполнить прошивку, но мы этого делать не будем, потому что уже используем более понятный способ. Далее, загружаем отладочную информацию:

Чтобы не вводить эти команды каждый раз при запуске отладчика, поместите их в файл debug.txt и запускайте в командной строке

Подчеркну еще раз, как мы используем elf и bin файлы в кроссплатформенной отладке: bin файл не нужен отладчику, поскольку он уже прошит в контроллере. Из elf файла ему нужна только отладочная информация, код из elf — файла не запускается.

После того как мы подключились к gdb серверу, отладка идет в обычном режиме. Ставим точку останова на функции main() и запускаем на выполнение:

Точку останова можно поставить и на номер строки (включите отображение номеров строк в vim).

Осмотримся:

Продолжим выполнение в пошаговом режиме:

или в полном написании next. Если мы хотим зайти в функцию, вместо next используем step. Чтобы выйти из функции не дожидаясь пошагового выполнения, задаем finish.

Краткая памятка команд отладчика:

Если вы что-то подзабыли, смело просите о помощи, например так:

и отладчик скажет, что команда watch EXPRESSION обеспечивает остановку программы если значение выражения EXPRESSION поменяется. В отладчике очень много таких полезных фишек, пользуйтесь ими!

И последнее. Запускайте отладчик с ключом -tui чтобы сразу видеть на экране исходник своей программы. И больше вам не нужны никакие IDE ) Более того, сейчас вы точно представляете себе, как все эти куски взаимодействуют вместе. Иначе все тонкости реализации были скрыты за фасадом какого-нибудь тормознутого Eclipse.

Успехов в ваших начинаниях!

ARM + DSP. Распределяем память

В процессе переноса алгоритма распознавания маркеров на OMAP платформу я обнаружил, что начисто забыл каким образом высчитывается память, которая распределяется между ARM и DSP. Восстанавливая крупицы ценной информации, которые щедро разбросаны по разным мануалам и форумам Texas Instruments, я решил зафиксировать с таким трудом добытые и упакованные в некоторое подобие осмысленной системы данные.

Мы будем пользоваться услугами Codec Engine от TI, про эту систему обеспечения передачи данных между ARM и DSP я немного рассказал в другой статье. Там же я упомянул, что CE берет на себя заботу о выделении необходимой нам памяти, поэтому самое время рассказать о том как это делается.

Как вы понимаете, в архитектуре OMAP подсистемы ARM и DSP работают с общей памятью. Поэтому придется учитывать их требования и интересы чтобы избежать конфликтов; более того, они совместно используют разделяемую память для обмена. Ситуацию усугубляет то, что ARM Linux работает с виртуальной памятью, а DSP — с физической — читает и пишет как есть, по реальным адресам.

На самом деле, нам нужно организовать три типа памяти:

  • которая нужна только ARM;
  • которая нужна только DSP;
  • разделяемая память, через которую ARM и DSP обмениваются данными (с любезной помощью Codec Engine).

Поехали.

ARM only

С ARM все обстоит проще всего. Здесь крутится Linux со своей системой виртуальной памяти, который самостоятельно назначает адреса из виртуального пространства. Как мы помним, в концепции виртуальной памяти ее может быть даже больше, чем количество физической. Нам нужно сделать только одну вещь — ограничить аппетиты Linux, а точнее обмануть его насчет физической памяти, доступной в системе. На моей плате BeagleBoard расположено ОЗУ емкостью 512МБ. Не мудрствуя лукаво, отдадим Linux половину.

Делается это в конфигурации загрузчика, который во время загрузки ядра передает последнему командную строку следующего вида:

Нас интересует параметр mem=256M, который указывает Linux на какой объем памяти он может рассчитывать. Сразу заметим, что из этой памяти 256М параметр vram=16M заберет 16 мегабайт: это размер видеопамяти для фреймбуфера. Кстати, вот еще один провал в памяти: эти строки, совместно с omapfb.vram=0:3M, omapfb.mode=dvi:640×48-24@60, omapdss.def_disp=dvi я настраивал для отображения видео через dvi и точно помню, что рассчитывал размер картинки отдельно для каждого из цветов RGB. Для того, чтобы сказать почему фреймбуферу нужно отдать именно 16 мегабайт, мне нужно будет погрузиться в аналогичные воспоминания )

После загрузки с такой командной строкой ядро отрапортует:

Все правильно, это доступная память 256М за вычетом видеопамяти 16М. Проверяем:

Опс… почему 224М? А потому что ядру тоже нужно место, и если мы посчитаем сколько оно занимает, в сумме получится 16М:

Итак, из доступной на BeagleBoard памяти 512М мы отдали Linux 256М и 256М осталось для DSP и Codec Engine. В самом Linux эти 256М разошлись так: на видеопамять 16М, на ядро тоже 16М, в результате приложениям осталось 224М. Теперь смотрим дальше, что будет происходить с оставшимся объемом 256М.

DSP only

Как я уже упомянул, DSP оперирует физическими адресами в памяти. Поэтому нам нужно вооружиться шестнадцатеричным калькулятором и составить таблицу распределения памяти. Мы начнем с адреса 0x80000000, потому что начиная с него начинает стартовать шина памяти. Запомним полезное число: 0x10000000, которое соответствует размеру памяти 256М, которую мы отдали ARM. Это означает, что доступная для дальнейших экспериментов (то, что осталось после того как Linux забрал свое) область будет начинаться с 0x80000000 + 256М = 0x90000000.

Как вы уже догадались, с диапазоном адресов от 0x80000000 до 0x90000000 будет работать Linux.

Теперь нам нужно принять важное решение: какую часть из оставшейся памяти отдать DSP? На несвоевременный вопрос «почему не всю оставшуюся»? отвечаю, что нам еще нужно распределить область обмена между ARM и DSP. Об этом будет в третьем параграфе, а сейчас мы договоримся, что отдаем DSP область 0x90C00000 — 0xA0000000.

С концом памяти все понятно: это 0x80000000 + 512М, то есть конец физической памяти. Откуда взялось значение 0x90C00000? Отвечу на это, что как и все важные решения, оно было принято «отфонарным» способом. Ладно, шучу, шучу ) Почему именно такая граница, я покажу опять таки в следующем параграфе. А сейчас мы посмотрим,  каким образом приложение на DSP стороне узнает о наших важных решениях.

Делается это с помощью конфигрурационного файла config.bld, интересуемый кусок которого в нашем случае будет выглядеть следующим образом:

Как следует из записи, DSP использует еще более тонкую нарезку памяти для различных нужд, например DDRALGHEAP это память предназначенная для самого Codec Engine. Мы не будем сильно погружаться в эту структуру, для нас существенным является то, что для DSP будет выделена память начиная с адреса 0x90C00000 и завершит эту область сегмент SR0 (Shared Region). Поскольку сегменты идут непрерывно друг за другом (сумма базы сегмента и его длины равна базе следующего сегмента), то последний занятый адрес памяти будет 0x96E00000 + 0x00200000 = 0x97A00000.

Вот мы и определили область памяти, с которой будет работать DSP. Она начинается с физического адреса 0x90C00000 и заканчивается физическим адресом 0x97A00000. В самом коде нет необходимости помнить о физических границах. В частности, механизм Codec Engine самостоятельно выделяет память для массивов данных в этих границах, для этого он вызывает функцию следующего вида, в которой только нужно заполнить структуру memTab и указать, сколько памяти и с какими характеристиками нам нужно:

После выделения памяти, кодек отдаст нам указатели на выделенные области памяти в memtab[0].base … memtab[6].base.

ARM + DSP

Здесь начинается самое интересное. Нам нужен разделяемый массив памяти, который одновременно будет использовать как ARM, так и DSP. Через этот массив Codec Engine будет гнать данные в обеих направлениях.

Управляет распределением модуль ядра CMEM, который запускается со стороны ARM Linux. Модуль принимает параметры, через которые задаются размеры и местонахождения пулов памяти. Выглядит это примерно так:

Перед запуском модуля cmemk.ko запускается собственно модуль syslink.ko, принадлежащий Codec Engine.

Из конфигурации CMEM видно, что используется область памяти от 0x90000000 до 0x90С00000, и это логично, потому что после 0x90С00000 идет уже область памяти эксклюзивно принадлежащая DSP. И все таки, откуда взялось это значение — 0x90С00000? Чтобы ответить на этот вопрос, обратим внимание на распределение пулов памяти, заданное параметром pools. Для моего проекта самым важным является передача сигнального и опорного 2D изображения с ARM на DSP, а также передача результата — 2D изображения в обратном направлении. Одна матрица остается в запасе, поэтому я распределил 4 пула размером 2621400 байт. В этот размер поместится байтовая черно-белая картинка размером 1280х1024 комплексных чисел.

Замечу, что поскольку CMEM — менеджер непрерывной памяти, то использовать пул кусками не получится. То есть нельзя будет в области размером 2621400 байт взять например два массива половинной длины. Один массив — один пул, такое правило. Я взял 4 пула чтобы иметь запас для матриц изображения, а для данных поменьше распределил 10 пулов по 131024 байт и 20 пулов по 4096 байт. Считаем границы:

0x96E00000 + 20 х 4096 + 10 х 131024 + 4 х 22621400 = 0x90С00000. Вот откуда взялось это значение.

Со стороны Linux указатели на эти области памяти возвращаются функциями вида

inBufSigImgArm = Memory_alloc(BUFSIZE_2D, &allocParams);

После этого указатели прописываются в специальной структуре, которая волшебным образом, с помощью Codec Engine также будет доступна со стороны DSP:

ARM:

DSP:

Вот и все. Если структура имеет тип In, то данные заполненные со стороны ARM, после вызова кодека со стороны ARM  попадут в функцию, которая будет вызвана со стороны DSP. И наоборот, результат работы DSP, который в структуре имеет тип Out, будет возвращен в ARM.

В моем проекте DSP выполняет тяжелонагруженные процедуры вычисления свертки изображений, которые требуют двумерного преобразования Фурье. Конечно, целочисленная арифметика DSP в архитектуре OMAP это не совсем то, что нужно для такого проекта. Но попробовать стоило, хотя бы для того чтобы сравнить быстродействие с другой реализацией — двумерное преобразование Фурье на GPU Raspberry Pi. Об этом — в следующих материалах

 

 

 

Препарируем Blockchain

Блокчейн используется не только в криптовалютах, а также в тех областях где нужно сопровождать доказанную последовательность определенных событий или состояний. Об этом буквально и говорит само слово blockchain: цепочка блоков. Таким образом, с системной точки зрения мы имеем блоки и связи между ними. Наша задача — препарировать блок таким образом, чтобы установить наличие данной связи (проверка блокчейна). Или создать новую связь — это уже майнинг, слово приводящее в трепет публику которая вращается в мире криптовалют.

Поработаем с блоками и связями такой популярной криптовалюты, как BitCoin.

Блоки и связи / Block & Chains

Типичный блок выглядит следующим образом:

Блоки хорошо представлять в виде вагончиков, которые сцепляются друг с другом. Эта аналогия обладает еще одним ценным соответствием: самый первый вагончик, то есть паровозик, не сцеплен спереди ни с кем. Это самый верхний блок в системе, и он будет самым верхним до тех пор, пока кто-нибудь не намайнит следующий блок. Тогда новый блок становится паровозиком во главе состава и к нему подцепляется остальная цепочка.

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

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

Поскольку hash — это фактически хэш блока, то такой механизм обеспечивает не только однозначность последовательности блоков, но и защиту содержимого блока. Попытка изменить последовательность блоков, удалить блоки или вставить новые, а также поменять содержимое самих блоков ни к чему не приведет: нарушится соответствие последовательности хэшей. Поэтому с блокчейном ничего сделать нелья: можно только добавить новый блок спереди. Это уже майнинг!

Block Parsing

Распарсим один из блоков блокчейна. Я взял для примера блок #528340, хэш которого имеет значение

0000000000000000002740c2167e7dcea59e11362587ea9ed348022701f5a73d.

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

Заканчиваем с лирическими отступлениями и приступаем к парсингу. Блок будем брать прямо с сайта по его хэшу:

Если код статуса равен 200, значит на запрос получен ответ с нужными данными в формате JSON, которые преобразуются в словарь с парами ключ — значение (переменная data).

Поскольку изобретатели алгоритма решили работать с данными справа налево, в режиме реверса последовательности байтов в строке, я написал вспомогательную функцию которая это делает:

Переформатируем все переменные, которые будут принимать участие в вычислении хэша:

Теперь все это нужно смешать в одну кучу

и найти значение хэша нашего блока:

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

Полезная нагрузка: транзакции

За повествованием мы забыли про полезное наполнение блока, ради чего все собственно затевалось: про транзацкции, кто кому сколько перечислил. Транзакций в блоке великое множество, они проходят под ключом tx. Gосмотрим только одну из, например самую верхнюю n=0:

Информацию дает строчка json.dumps(data[‘tx’][n]), остальное — красивости для удобочитаемого вывода. В результате увидим:

Видно, что некто перечислил 14.3 BTC кому-то. Именно так: кошельки отправителя и получателя представлены в виде хэшей, и кто это такие — мы не знаем. Собственно это и есть краеугольный камень БитКойна.

Блокчейн хранит абсолютно все транзакции, которые были в системе. По этой причине совершенно четко видно, как появляются монеты и к кому они переходят, и какое общее количество их находится в системе.

На этом завершим наш небольшой анализ, ведь главное мы уже сделали: показали как вагончики сцепляются друг с другом и уникальность этой сцепки.

Google Contacts API и авторизация OAuth2

Когда в адресной книге Google набирается несколько тысяч клиентов, становится тяжеловато ворочать этим объемным списком. Импорт/экспорт в странички Excel становится неудобным; кроме того появляются CRM-подобные приложения, в которых хотелось бы интегрировать самые разные базы данных, в том числе и контакты Google. Значит, пришло время доступиться к нашим контактам через консольные приложения, используя Google Contacts API.

Получение списка контактов

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

Можно догадаться, что данные мы получим в формате json, что понятно, поскольку каждой записи в адресной книге соответствует куча полей, также понятно сколько записей будет выведено. И поскольку здесь нет ни нашего логина, ни пароля, можно предположить, что ACCESS_TOKEN — это та самая строка, которая обеспечивает авторизацию. Чтобы понять, как эта авторизация работает, мне в свое время пришлось выполнить несколько танцев с бубном. В этой статье делюсь результатами своих изысканий.

Регистрируемся в Google

Предполагаю, что у вас уже есть Google-аккаунт, или электронная почта. Логинимся на страничке Гугла и идем по адресу https://console.developers.google.com/cloud-resource-manager , где создаем новый проект. Даем ему произвольное название, например Beerware. Дальше переходим в другое место https://console.developers.google.com/apis/credentials. Жмем кнопочку «Создать учетные данные» и выбираем «Идентификатор клиента OAuth», и дальше отмечаем «Другие типы». Дадим название идентификатору — скажем Beerware Script.

Не спрашивайте меня, что означает разнообразие в выборе различных опций. Погружаться в это можно долго, важно лишь что наш вариант — рабочий.

Это все, что нужно было сделать в девелоперских панелях Гугла. Если вы нажмете на редактирование идентификатора, то увидите ID клиента и секрет клиента. Запомним эти названия, они понадобятся нам дальше. Чтобы не путаться с обозначениями при подстановке в другие строки, обозначим их CLIENT_ID и CLIENT_SECRET.

Authorization Flow: как проходит авторизация OAuth2

Конечной целью авторизации является получение ACCESS_TOKEN, который необходимо использовать каждый раз когда мы делаем запрос к адресной книге. Теперь нам нужно внимательно пройтись по всем этапам. Самая главная вещь, которая толком не описана в документации Google — какие этапы выполняются только один раз, а какие нужно выполнять периодически. Я буду это подчеркивать.

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

Получение кода авторизации

Код авторизации сразу ставит в тупик. Зачем он нужен, если уже есть CLIENT_SECRET?  Разница в том, что код авторизации используется только один раз — для получения токенов. Замечу сразу, что если вы попытаетесь получить по одному и тому же коду авторизации токены еще раз, то получите ошибку. Таким образом, код авторизации действителен пока мы еще не получили токены. После их получения он сразу «протухает». Но об этом — дальше.

CLIENT_SECRET, как и CLIENT_ID — постоянные строки, скрытые в нашем приложении вдали от посторонних глаз.

Итак, посылаем на сервер CLIENT_ID, CLIENT_SECRET и получаем код авторизации. Назовем его AUTH_CODE.

Получение токенов

И снова заголовок наводит на размышления. Почему токены во множественном числе, когда вначале говорилось только об ACCESS_TOKEN? На самом деле, помимо этого токена, который используется при работе с адресной книгой, нам дают еще REFRESH_TOKEN. Его роль будет ясна ниже.

Посылаем на сервер CLIENT_ID, CLIENT_SECRET, AUTH_CODE и получаем парочку ACCESS_TOKEN, REFRESH_TOKEN.

Казалось бы, тут можно забыть о REFRESH_TOKEN, поскольку ACCESS_TOKEN  у нас в руках, и дальше еще забыть и про авторизацию и спокойно пользоваться ACCESS_TOKEN во всех запросах. Я так поначалу и делал, только обнаружил что в один прекрасный момент сервер перекрыл мне доступ. В чем дело? А причина была в том, что по задумке Google время жизни ACCESS_TOKEN — всего лишь час, после чего он протухает и работать с ним не получится.

Что теперь, снова получать код авторизации и токены? Нет. Про эти два этапа «получение кода авторизации» и «получение токенов» можно полностью забыть и уже никогда к ним не возвращаться. Самое главное, что у нас есть — это REFRESH_TOKEN. Сохраните его в надежном месте, поскольку теперь кроме него нам ничего не нужно.

Замечу, что эти пройденные этапы мы выполняем лично. Наше приложение Beerware Script о них ничего не знает: ему нужны только токены.

Получение ACCESS_TOKEN по REFRESH_TOKEN

Дальше все становится просто. Когда ACCESS_TOKEN протухает, то есть через час становится не действительным, или просто не дожидаясь этого момента, мы посылаем на сервер CLIENT_ID, CLIENT_SECRET, REFRESH_TOKEN и получаем обновленное значение ACCESS_TOKEN, которое используем для запросов к серверу. Важно знать, что при этом REFRESH_TOKEN не меняется. Поэтому для приложения критично запомнить его, например в файле, при получении в самый первый раз.

Все этапы авторизации из командной строки

Для различных языков есть соответствующие библиотеки OAuth2. Мы же, для ясности изложения, все будем делать ручками из командной строки, формируя запросы с помощью утилиты curl. В конце концов, все эти библиотеки делают тоже самое — формируют url запросы к серверу Google.

Начнем с получения кода авторизации. Поскольку эта процедура выполняется только раз, не грех поработать вместо нашего приложения. Вбиваем в адресную строку броузера и наблюдаем:

Запросом response_type=code мы хотим увидеть код авторизации. Он будет переслан по адресу redirect_uri, а поскольку на URI=http://localhost:8080 ни у меня, ни думаю у вас — ничего нет, то естественно браузер даст ошибку. Но это неважно, поскольку искомый код появится в адресной строке браузера и мы возьмем его оттуда, копируя начиная с code= и продолжая до символа #, который присутствует в конце url. Так мы получаем AUTH_CODE.

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

Теперь получаем токены. На запрос

сервер выдаст нам долгожданную парочку токенов:

Если мы попробуем получить токены еще раз, с использованным кодом авторизации, нас ожидает ошибка:

Все, код авторизации протух и с ним уже сделать ничего нельзя!

На этом ручная работа закончилась и скрипт Beerware Script может начинать работу. Отдаем ему ACCESS_TOKEN и REFRESH_TOKEN и забываем про авторизацию. Все, что нужно делать скрипту когда ACCESS_TOKEN становится недействительным — это получить его новое значение запросом

Само собой, это будет уже не утилита curl, а https запрос с соответствующей библиотеки нашего приложения Beerware Script.

После запроса мы получим новый ACCESS_TOKEN:

Поработаем с адресной книгой

Теперь самое время посмотреть, как будет выглядеть работа со списком адресов. Как обычно, на этом этапе нам ассистирует Python. Ниже — фрагменты скрипта Beerware Script, которые дают представление об общей идее работы с полученным списком.

Вначале затребуем список:

В комментах видно, в каком месте нужно ловить ошибку авторизации, когда ACCESS_TOKEN устареет. Дальше нужно что-то делать с полученным списком адресов, наверное самое очевидное — это получить номера телефонов.

Делается это в цикле по всей структуре data:

В результате после работы скрипта получим список контактов, где под каждой фамилией будет список телефонов. Тема отдельного рассмотрения — как изменять существующие контакты и заводить новые. Это решается засылкой на сервер соответствующей xml записи. Подробно процедура описана здесь. Думаю, вы и сами отлично справитесь с этой задачей )

Согласованный фильтр 2D: поиск соответствия в изображении

Согласованную фильтрацию можно рассматривать как во временной, так и в частотной области.

Чтобы фильтр был согласованным по отношению к сигналу, его импульсная характеристика должна быть зеркальным отражением сигнала по оси времени. Этот принцип несет определенный физический смысл: чтобы свертка сигнала и импульсной характеристики фильтра была максимальной, импульсная характеристика должна быть расположена именно таким образом.

Еще нагляднее физический смысл проявляет себя в частотной области: согласованный фильтр «доворачивает» спектральные составляющие сигнала ровно настолько, чтобы они складывались синфазным образом, достигая максимального выходного эффекта. Чтобы доворот происходил в направлении компенсации, используется комплексно-сопряженное значение передаточной функции фильтра.

Часто реализацию согласованного фильтра выполняют в частотной области, используя наработанные библиотеки быстрого преобразования Фурье (БПФ).

Вот пожалуй и все, что хотелось бы сказать про согласованный фильтр (СФ). Поехали дальше.

От времени к пространству

Что скажете, если мы займемся фильтрацией пространственных данных? Это немного непривычно, поскольку мы обычно связываем сигнал с изменениями во времени. С другой стороны, когда смотрим на график этого сигнала то видим только рисунок, и ничего более. И фактически работаем с этим рисунком. Только это будет одномерная обработка, где ось времени будет просто абстрактной осью x.

С двумерными изображениями физический смысл ускользает. Как представить этот сигнал изменяющимся во времени, когда он содержит уже две «псевдовременные» оси? Поэтому нам лучше оторваться от привязки ко времени и воспринимать сигнал тем, чем он является — 2D изображением. Что интересно, для многомерных сигналов также существует понятие пространственных частот, и это активно используется при изучении работы объемных антенных решеток.

Маджонг: найти кубик

Чтобы наше повествование не выглядело слишком солидным и серьезным, выберем в качестве входного сигнала изображение россыпи кубиков Маджонг:

Исходная сцена для поиска кубика, соответствующего шаблону

Поставим себе задачу: найти в этой россыпи определенный кубик, например такой:

Шаблон для поиска

Как вы догадались, этот кубик, который я буду использовать в качестве шаблона, просто скопирован из исходной сцены — россыпи. Вот кстати, можно сразу немного поиграть в Маджонг: найдите этот шаблон на сцене!

При этом будем считать, что окружающие кубик куски — это тоже часть шаблона. Не будем обрезать его до идеального, ведь лучшее — враг хорошего )

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

Теперь пришло время дать правильные названия всем действующим лицам и участникам.

Кто есть кто

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

Наша задача: найти этот кубик на сцене с помощью 2D согласованного фильтра, и не только найти, но и сразу определить его местоположение. Структура СФ будет определяться шаблоном для поиска — этим же кубиком, который на этот раз сам по себе.

Поскольку в этой задаче нет никакого времени, попробуем обозначить шаги ее решения в частотной области. СФ работает следующим образом:

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

Перед фильтрацией необходимо найти пространственные спектры сцены и нашего опорного кубика:

Запускаем интерпретатор Питона (да, это был именно он) и смотрим на результаты.

Результат работы согласованного фильтра 2D

Я добавил в программу небольшой вспомогательный код, чтобы отмечать зеленой рамкой на сцене месторасположение максимального отклика на выходе СФ. На рисунке сцена (сигнал+шум) расположена слева, отклик согласованного фильтра — справа. Белый цвет соответствует максимальному отклику.

Согласованный фильтр 2D, 2D Matched Filter

Результат работы согласованного фильтра 2D для поиска шаблона

На картинке отклика СФ хорошо видна яркая точка, соответствующая пику выходного сигнала. Ее расположение в точности соответствует положению верхнего левого угла шаблона. Поэтому можно сказать, что с задачей мы справились: нашли совпадающий кубик по шаблону, или отфильтровали помеху от сигнала, используя опорный сигнал.

Я также провел второй эксперимент, в котором поставил на сцену шаблон еще раз, в другом месте. По идее, СФ должен показать наличие двух согласованных с опорным сигналов. Так оно и есть: на картинке отклика СФ наблюдаем две яркие точки, соответствующие положению двух шаблонов на сцене.

Согласованный фильтр 2D, 2D Matched Filter

Результат работы согласованного фильтра 2D со сценой, содержащей два шаблона

Интересное наблюдение: на картинке отклика СФ наблюдается небольшое светлое пятно, соответствующее определенному совпадению с сигналом шаблона. Я долго не мог понять, что он там нашел, а потом догадался: ведь искомый кубик имеет повторяющуюся структуру, и этой области немного не хватило рисунка, чтобы продолжить следующие шесть точек на юго-восток. Совпадение получилось только наполовину, что дало всплеск на выходе СФ.

Может возникнуть такой вопрос: если помеха отфильтрована, почему на выходе СФ мы не наблюдаем чистый сигнал? Вот такой он, согласованный фильтр: использует всю информацию о сигнале, и на выходе ее уже не остается. Только уровни, которые только и остается что сравнивать. Если бы на выходе мы наблюдали признаки формы сигнала, это означало бы, что СФ не использовал эту информацию в обработке.

Также открою маленький секрет: выход СФ есть не что иное, как двумерная корреляционная функция сцены и шаблона.

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

Алгоритм MUSIC: MUltiple SIgnal Classification в оценке спектра

Вот мы наконец и добрались до этого метода. Литературы по этой технике — целая куча, но как обычно смысл ускользает. Само описание MUSIC достаточно простое: это поиск экстремума произведения двух матриц. Однако что происходит внутри этих матриц — с этим нам предстоит разобраться.

Эту тему довольно сложно понять с нуля, не имея относительно прочной опоры в линейной алгебре. Поэтому нам понадобится аэродром подскока — пара статей, которые я написал ранее: собственные числа и векторы в поиске закономерностей. Метод главных компонент и ковариационная матрица и линейная трансформация. Прочитайте их еще раз, и мы детально, шаг за шагом совершим увлекательное путешествие по пространствам (в буквальном, математическом смысле), а путеводителем для нас будут собственные числа и векторы матриц, которые описывают преобразования этих пространств. Как обычно, ассистировать нам будет Python: строгая нотация языка программирования не допустит никакой двусмысленности, особенно в описании структуры и размерности матриц, чем частенько грешат различные статьи.

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

Компактный симпатичный спектр из двух частот

Зададим исходные данные. Будем иметь дело с сигналом, состоящим из двух частот; также подмешаем немного шума, для реальности происходящего:

На самом деле, мы будем наблюдать смесь этих сигналов, и наша задача — определить какие частоты присутствуют в его спектре. Поэтому наблюдаемый сигнал X будет суммой этих массивов:

Суммирование идет по выборкам, о чем говорит выбор оси axis=1. Поэтому размерность полученной матрицы будет N x 1.

Как и во многих алгоритмах линейного предсказания, в том числе и в MUSIC, конкретная реализация сигнала не представляет большого интереса. Гораздо более важной и обобщенной характеристикой является корреляционная, а точнее автокорреляционная матрица Rxx, выявляющая внутренние связи во входных данных X:

Update:

Матрица Теплица использована для удобства расчета автокорреляционной матрицы, каждый элемент которой представляет собой dot-продукт сигнала sig и его смещенной копии. Чтобы не делать смещение в цикле, с помощью матрицы Теплица входной сигнал (вектор Nx1) превращается в заготовку — матрицу XT размером NxN, которая уже содержит все допустимые сдвиги сигнала sig от 0 до N-1.

Остается только получить dot продукт этой заготовки обычным выражением dot=XT*XT.H

А теперь — прощай, сигнал X! В нашем алгоритме ты нам больше не понадобишься. Вместо тебя будет работать твоя корреляционная матрица Rxx. Сразу скажу, что быть на сцене ей тоже осталось недолго: после того как мы находим собственные числа en и векторы ev корреляционной матрицы Rxx, она тоже перестает быть нужной:

Гарантирую, что с этого момента вы потеряли нить и вам расхотелось читать дальше ) Поэтому самое время остановиться и понять смысл того, что мы делаем. Тем более что большую часть алгоритма мы уже прошли: осталось фактически только одно последнее действие ) Вот она, обманчивая простота матриц!

Итак, наступил обещанный момент погружения в матричные пространства.

Зачем нужны собственные числа и векторы

Идея метода MUSIC состоит в разделении пространства входных сигналов на сигнальную часть и помеховую. Для пространства сигналов существует свой базис, в котором может быть разложена корреляционная матрица Rxx. Поскольку Rxx является самосопряженной (сравниваем Rxx и Rxx.H в Питоне — это одно и то же), то базис разложения будет ортогональным и будет представлять из себя не что иное, как собственные векторы матрицы, масштабированные собственными числами. Что такое ортогональное разложение вы конечно же помните из школьной геометрии, когда рисовали проекции вектора на ортогональные оси x и y. Только это было двумерное пространство, которое легко представить геометрически, а в нашем случае размерности пространства N только и остается, что положиться на формулы.

Проверим, соответствует ли Rxx разложение по ортогональному базису:

результат будет соответствовать Rxx.

Теперь мы можем предположить, что одна часть собственных векторов, участвующих в разложении Rxx, соответствует сигналу, а другая часть — помехе. Будем также держаться предположения, что полезных сигналов — P, а все остальное в количестве N-P это помехи или шум. Наше предположение некоторым образом подтвердят собственные числа, если мы расставим их в порядке убывания:

По собственным числам можно судить об уровне входных сигналов: как и ожидалось, первые P=2 это полезный гармонический сигнал, остальное — это помеха. Не откладывая на потом, сразу упорядочим и собственные векторы, чтобы они соответствовали собственным числам, расположенным по убыванию:

Теперь выстраиваем следующую логическую цепочку. Начало ей положит тот факт, что собственные векторы сигнала и помехи — взаимно ортогональны:

результат — вектор практически близкий к нулю, что и означает ортогональность.

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

Разложение экспоненциального вектора на базис помехи

Теперь все дело за тем, как правильно записать нотацию искомого разложения. Собственные векторы помехи мы уже выделили раньше: это вектор EV. Вектор комплексных опорных сигналов будет иметь размерность N:

Значения частот будем перебирать из диапазона:

Абсолютное значение спектра будем находить как модуль разложения REF*EV.H*EV*REF.H.

Тогда полный цикл перебора опорного вектора будет выглядеть так:

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

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

показан на рисунке.

Начнем с P=2:

MUltiple SIgnal Classification, MUSIC, Eigenvectors, Eigenvalues, Оценка спектра, Корреляционная матрица, Correlation Matrix

Оценка спектра MUSIC для P=2

Здесь неожиданностей нет. На рисунке четко видно два пика относительной частоты 3.0 и 5.0, причем уровень второго сигнала ниже. Мы четко определили наличие двух синусоид в комплексном сигнале в присутствии шума, причем получили значение частот этих сигналов и относительные амплитуды. Теперь возникает интересный вопрос: что произойдет если мы зададим P=1? По идее алгоритм должен посчитать за полезный только один сигнал, а вторую синусоиду вместе с шумом отнести к помехе. Так оно и есть:

MUltiple SIgnal Classification, MUSIC, Eigenvectors, Eigenvalues, Оценка спектра, Корреляционная матрица, Correlation Matrix

Оценка спектра MUSIC для P=1

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

Продолжаем эксперимент.  Теперь предположим, что на входе три полезных сигнала, то есть P=3. Что произойдет если белый шум также будет засчитан на равных как нечто полезное?

MUltiple SIgnal Classification, MUSIC, Eigenvectors, Eigenvalues, Оценка спектра, Корреляционная матрица, Correlation Matrix

Оценка спектра MUSIC для P=3

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

Продолжим расширять наше представление о количестве полезных компонент и зададим P=4:

MUltiple SIgnal Classification, MUSIC, Eigenvectors, Eigenvalues, Оценка спектра, Корреляционная матрица, Correlation Matrix

Оценка спектра MUSIC для P=4

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

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

 

 

 

АРП DF-2000: что пошло не так с этим радиопеленгатором

Историю современных радиопеленгаторов для аэропортов гражданской авиации можно отсчитывать от момента появления новой версии АРП «Платан» с процессорной обработкой и операционной системой. В то время было принято, что радиоприемные устройства (РПУ) — покупные «Юрки» будут компромиссным решением (за неимением радиоприемника своей разработки). Вначале разрабатываемое цифровое РПУ было частью пеленгационного проекта, но со временем руководство конторы решило выделить его в отдельное направление. Возможно, повлияло желание сделать унифицированный со связным приемник, хотя у каждой унификации есть своя цена. Но не будем забегать вперед )

По завершении разработки нового АРП «Платан» я переключился на собственные проекты, но продолжал интересоваться ходом модернизации пеленгатора, который должен был обзавестись своим радиоприемным трактом. И он появился: РПУ, более известное под названием ФИС, было выполнено на профессиональном уровне инженерами которые тогда еще работали в конторе (ну вот, снова вперед забежал). Классическая супергетеродинная схема с перестраиваемым преселектором обеспечивала отличные характеристики электромагнитной совместимости (ЭМС), такие как избирательность по соседнему каналу, подавление интермодуляционных продуктов. Ведь не секрет что эфир со временем не становится чище, и разнообразных помех, таких как профессиональные и любительские радиостанции, станций GSM, Wi-Fi и других становится все больше и больше. Поэтому обеспечение ЭМС выходит на первый план, тем более что современные радиокомпоненты позволили практически закрыть вопросы чувствительности АРП и улучшения отношения сигнал/шум.

Как и в АРП «Платан» с Юрками, так и с ФИСами у эксплуатации не было никаких нареканий. Но начиная с определенного времени, замечания посыпались. Вплоть до таких, как:

«у радиопеленгатора DF-2000 во время грозы, ложные пеленги на всех частотах. Думаем, что надо чувствительность прм. уменьшать,пока ждем ответа заводчиков» (с форума инженеров ЭРТОС).

На АРП с ФИС нареканий нет:

«У нас один из первых DF-2000 с ФИС ами. Ложных пеленгов на метеоявления, в отличии от АРП-75, не наблюдалось ни разу с 2008 года»

Это удивительно, потому что в радиопеленгаторе «Платан» была эффективная схема фазового обнаружения сигнала, которая давала очень низкие уровни вероятности ложной тревоги. И «пробить» ее на ложный пеленг, тем более при грозовых электромагнитных возмущениях, было нереально. Было очевидно, что что-то пошло не так. В конечном счете, причина была найдена: сбои в работе АРП DF-2000 были связаны с очередной модернизацией, а именно с заменой ФИС на цифровой приемник. Само собой, эта эволюция проходила уже без моего участия.

С этого места начинаем смотреть подробнее — как выполнен цифровой приемник, какая у него структура и где источник проблемы.

Цифровой радиоприемник в АРП DF-2000: тотальная «оптимизация»

Структурная схема пеленгационного приемника приведена на рисунке. Собственно говоря, это тот же самый приемник RS-2000, который используется в приемных центрах TRS-2000 производимых и поставляемых АО «Азимут». Разница лишь в фрагменте по правую сторону от АЦП — в программном обеспечении. И в том, что этот аппаратно одночастотный приемник превратился в РПУ, работающий на 4-х частотных каналах одновременно.

Ключевой аспект в этой истории — как раз слово «одновременно».

Конечно, сократить вчетверо объем радиоприемного оборудования — это хороший выигрыш по затратам. Рост стоимости радиопеленгатора идет за счет увеличения количества каналов, или РПУ. Вопрос лишь в цене: какими техническими характеристиками за это заплачено?

АРП, DF-2000, Цифровой радиоприемник, блокирование, SDR, Software Defined Radio, АРП Платан

Аналоговая часть РПУ радиопеленгатора DF-2000 представляет приемник прямого усиления.  Разделение частотных каналов происходит после оцифровки АЦП. Преселектор отсутствует, поэтому весь эфир из диапазона 118 -136 МГц поступает на вход АЦП.Внимание: такая схема построения подвержена блокированию и имеет плохие характеристики ЭМС!

Смотрим тракт начиная с антенной системы. Принимаемый сигнал усиливается в приемнике прямого усиления, с помощью АРУ загоняется в диапазон входных сигналов АЦП, и далее цифровая обработка сигналов выделяет свой частотный канал, подавляя сигналы других радиостанций. Поскольку в отличие от классической супергетеродинной схемы здесь нет преселектора, подобная структура имеет все недостатки приемников SDR. В одной статье я уже ссылался на подробное описание этих недостатков, сделаю это еще раз:

«Основным недостатком, ограничивающим широкое применение подобного типа РПУ (с прямой оцифровкой сигнала с антенны — мое примечание), является невозможность обеспечения высоких уровней динамического диапазона по блокированию. Поскольку на входе АЦП отсутствует фильтрация, то АЦП соответственно не может выполнять оцифровку одновременно сигналов большого и малого уровня. В соответствие нормам ГОСТ радиоприемник должен обеспечивать уровень восприимчивости по блокированию не менее 126 дБ/мкВ для РПУ второго класса и 130 дБ/мкВ для РПУ первого класса. Очевидно, что существующие типы АЦП 14-16 бит не в состоянии обеспечить выполнение этого параметра.

Кроме этого, РПУ данного типа недостаточно качественно осуществляют обработку сигналов малых уровней. При приеме сигналов уровней чувствительности, рост ошибок квантования, вызванный недостатком разрядности АЦП, вызывает невозможность качественного приема. Естественно, что эти ограничения разработчики пытаются снижать конструктивными и программными средствами, но до появления на рынке высокочастотных АЦП с разрядностью не менее 24 бит, приемники данного типа не смогут соответствовать современным нормам по электрическим параметрам и составить полноценную конкуренцию аналоговым РПУ».

Поскольку приемник прямого усиления никак не защищает вход АЦП от помех, то сигналы любого радиопередатчика из диапазона 118 — 138 МГц и не только — будут напрямую давить на вход АЦП и снижать его реальный динамический диапазон. Чужие радиостанции, VOR, DVOR, даже импульсные сигналы локаторов — будут способствовать блокированию АЦП, и дальнейшая цифровая фильтрация будет бессмысленной. Собственно, этот процесс и наблюдается в эксплуатации.

АРП DF-2000 блокирует сам себя: принципиальные ошибки построения тракта обработки

Вернемся к нашему рисунку. Проведем мысленный эксперимент: будем наблюдать одновременное прохождение пеленгуемых частот f1, f2, f3, f4 через приемный тракт. При этом создадим для АРП льготный режим: представим, что помех вообще никаких нет, то есть в районе аэродрома выключены все связные и радионавигационные устройства. И представим, что эти сигналы имеют одинаковый уровень: на связь одновременно вышли четыре борта с одинаковой мощностью радиостанции и на одинаковом расстоянии.

С антенны все четыре сигнала усиливаются в приемнике прямого усиления и подаются на вход АЦП. Автоматическая регулировка усиления (АРУ) наблюдает суммарный сигнал и не в состоянии различать сигналы по частотам, поэтому к 16-разрядной сетке АЦП будет приведена сумма сигналов f1, f2, f3, f4 и как результат каждый из них будет подавлен в 4 раза. Я хочу подчеркнуть еще раз: все, что находится в эфире в полосе 118 — 136 МГц — свои сигналы с борта и многочисленные источники излучения в этом диапазоне, будет усилено и поступит на вход АЦП, поскольку в АРП DF-2000 приемник не имеет преселектора, то есть 2 бита из 16 динамического диапазона АЦП мы уже потеряли. В цифровой части после АЦП помеховые сигналы отфильтровываются четырьмя фильтрами, каждый из которых настроен на частоту канала пеленгования, и извлекается пеленгационная информация. Пока все ОК.

Теперь изменим исходные условия и сделаем их приближенными к реальности. На высоте 10000м на дальности 350км летит борт, который выходит на связь. Такая дальность для АРП не диковинка: ее максимальное значение при такой высоте и мощности радиостанции 5Вт по ТТЗ составляет 360км. Поэтому радиопеленгатор должен отработать без проблем. Поскольку приемник прямого усиления DF-2000 усиливает все подряд в диапазоне 118 — 136МГц, на входе АЦП будет все, что работает в этой широкой полосе в районе аэропорта. Не будем считать мощные помехи от VOR, DVOR, а также гармоники мощных сигналов первичных и вторичных радиолокаторов. Ограничимся рассмотрением передатчиков местного приемо-передающего центра; предположим что в определенный момент на передачу выходят одновременно 8 радиостанций. Тогда на входе АЦП будет смесь слабого сигнала с борта и мощных радиостанций центра.

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

В нашем случае главный подозреваемый на блокирование — АЦП. Если его небольшой динамический диапазон будет перегружен, то никаких нормальных сигналов на выходе мы не увидим.

Продолжаем наши опыты ) Для того, чтобы попасть в динамический диапазон, приемнику придется задействовать АРУ таким образом, чтобы сумма сигналов на входе АЦП не превысила максимального значения. При этом необходимо контролировать не суммарную мощность, а амплитуду. За счет биений суммарный сигнал максимальной амплитуды будет равен сумме амплитуд полезного сигнала и помехи.

Как и в предыдущем примере, АРУ откалибрует суммарный сигнал только по самому сильному составному — в нашем случае по помехам которые создает передающий центр. Усиление будет достаточным только для прохождения помех, а наш сигнал с борта потеряется на уровне младших битов АЦП.

Говоря другими словами, в четырехканальном тракте частота самого сильного сигнала блокирует сигналы более слабых передатчиков. То есть, в этом случае никакой «одновременной» работы четырех каналов радиопеленгатора не получается.

Это гипотеза, осталось подкрепить ее цифрами.

Считаем уровни

Найдем соотношение полезного сигнала и помех. В качестве помехи, как мы условились раньше, будем считать 8 передатчиков передающего центра, расположенные от АРП на расстоянии 500м. Для расчета будем пользоваться зависимостью потерь распространения в свободном пространстве FSPL (Free Space Path Loss, потери распространения в свободном пространстве):

\displaystyle\large FSPL=\bigg(\frac{4{\pi} R}{\lambda}\bigg)^2

Разница в дальностях до борта и передающего центра дает искомое соотношение; для R1=350км, R2=0.5км получаем:

\displaystyle\large \frac{FSPL_2}{FSPL_1}=\bigg(\frac{R_1}{R_2}\bigg)^2=490000=56.9dB

Теперь нужно учесть разницу между мощностями сигналов бортовой и наземных радиостанций, которая составит 100 Вт (земля)/5 Вт (борт) = 20 = 13dB, итого суммарно разница между сильным и слабым сигналом на входе АЦП составит 56.9+13=69.9dB.

По напряжению разница 69.9dB составит 3126 раз. Уточним еще раз: это разница между уровнем по амплитуде одной радиостанции передающего центра и радиостанции борта на входе АЦП. Теперь надо учесть, что радиостанций — восемь. При одновременной передаче максимальные амплитуды суммируются, поэтому разница в уровнях станет еще больше — 3126*8 = 25008. Что там с нашим динамическим диапазоном АЦП? Для 16 разрядов он равен 65536. После того как АРУ уместит суммарный пеленгуемый сигнал в разрядную сетку АЦП, для сигнала борта останется 65536 / 25008 = 2,62 уровня. Это практически 1 бит АЦП, который работает на уровне шума! Для сигнала работает только 1 бит АЦП: вот оно, блокирование.

Заметьте, что теперь нет смысла говорить про частотное разделение сигналов: сигнал с борта до фильтра просто не дойдет. Таким образом, в такой помеховой обстановке АРП DF-2000 борт просто не увидит. Что и наблюдается на практике.

Снова не могу удержаться, чтобы не заметить, что радиотехнику должны разрабатывать не программисты, а инженеры ) Концепция «все загоним в цифру а там уже разберемся» не работает. Как следствие — на выходе дешевое как в смысле стоимости, так и в смысле параметров изделие. Инженеры ушли, оптимизаторы остались.

А жаль, хороший пеленгатор был вначале.

Устойчивость АФАР/AESA радиолокатора F-35 к блокированию T/R модулей

F-35, АФАР, ПФАР, AESA, PESA, Radar, Радиолокатор, Локатор, Antenna  Pattern, блокирование, обнаружение, мощность, TRM, T/R module, Transmit Receive Module

Антенная решетка локатора AN-APG-81 самолета F-35

Бортовые радиолокаторы военных самолетов, предназначеные для поиска и сопровождения целей проходят эволюцию от пассивных фазовых антенных решеток (ПФАР, или PESA — Passive Electronically Scanned Array) к активным фазовым антенным решеткам — АФАР, или AESA: Active Electronically Scanned Array). Базовым элементом и тех и других является приемо-передающий модуль — Transmit/Receive Module, TRM. И если в пассивной решетке он практически в единственном экземпляре, то в активной на каждый элемент приходится по одному TRM.

В локаторе AN/APG-81 истребителя-бомбардировщика F-35 дотошные юзеры насчитали 1676 элементов; можно представить какой объем оборудования займут TRM и какова будет стоимость. АФАР — удовольствие не из дешевых! Но и возможности по сравнению с ПФАР неизмеримо выше: одновременное сопровождение нескольких целей, сканирование произвольных областей пространства, произвольный выбор сигналов и многое другое. Но нас в данном случае интересует не количественное увеличение сложности АФАР, а принципиальные изменения в тракте обработки сигнала, которые за ним последовали.

F-35, АФАР, ПФАР, AESA, PESA, Radar, Радиолокатор, Локатор, Antenna  Pattern, блокирование, обнаружение, мощность, TRM, T/R module, Transmit Receive Module

Transmit-Receive Module, T/R модуль

На фото слева показан T/R модуль, возможно даже от F-35. Каждая сборка обслуживает несколько элементов антенной решетки и содержит малошумящие усилители LNA (цепь приема) и усилители мощности HPA (цепь передачи). Переключение на прием/передачу производится по внешним сигналам управления. Сигнал, принимаемый элементом антенной системы, усиливается LNA и подвергается аналого-цифровому преобразованию. Дальнейшая обработка идет в бортовом сигнальном процессоре.

АФАР/AESA и ПФАР/PESA: что за чем следует

Мы хотим выявить теоретические и практические отличия в образовании диаграммы направленности активной и пассивной антенных систем. В соответствии с принципом обратимости нет разницы, для какого режима — прием или передача проводить анализ, это во первых, и во вторых интересующий нас вопрос блокирования относится сугубо к приему АФАР. Поэтому рассмотрим, как работает диаграммообразующая система (ДОС) в обоих случаях — для AESA и PESA.

F-35, АФАР, ПФАР, AESA, PESA, Radar, Радиолокатор, Локатор, Antenna  Pattern, блокирование, обнаружение, мощность, TRM, T/R module, Transmit Receive Module

Расположение T/R модулей по одношению к диаграммообразующей системе. В АФАР/AESA (активная решетка) T/R модули располагаются до ДОС, в ПФАР/PESA (пассивная решетка) T/R модуль располагается после ДОС. В АФАР фазовый сдвиг вносится в компьютере, в ПФАР — с помощью управляемых фазовращателей

В АФАР сигнал элементов антенны принимается T/R модулями и поступает в сигнальный процессор, который назначает каждому элементу АФАР весовой коэффициент и фазовый сдвиг, после чего после численной обработки все сигналы суммируются. Синфазное суммирование, дающее максимальный сигнал будет формировать основной лепесток диаграммы; сигналы с других направлений будут складываться неоптимальным образом, давая на выходе невысокие уровни, которые мы называем боковыми лепестками.

В ПФАР фазовый сдвиг вносится процессором физически, с помощью фазовращателя через который подключен элемент антенной системы. В этой же антенной системе сигналы суммируются до сигнального процессора, избавляя последний от необходимости производить расчет по каждому элементу ПФАР. В результате точно также формируются основные и побочные лепестки.

То обстоятельство, что АФАР за счет управления амплитудой сигнала антенного элемента может точнее и лучше формировать диаграму по сравнению с ПФАР, является несомненным преимуществом но не играет существенного значения с точки зрения целей нашего рассмотрения.

Итак, математическая процедура ДОС в случае активной и пассивной решетки совершенно идентична: разница лишь в том, что в локаторе с АФАР формирование диаграммы производится в вычислителе, а в локаторе с ПФАР — аппаратно в самой антенной системе. Таким образом, теоретических различий между этими способами реализации — никаких. С практической точки зрения, особых проблем тоже как-бы не видно. Но это только на первый взгляд.

Сейчас мы пришли к той точке, когда программисты, поставив этот знак равенства, успокаиваются и идут пить кофе, а инженеры начинают беспокойно ерзать на своих стульях. И это неспроста! Инженеры знают, что полная аналогия между программной и аппаратной обработкой оправдана только тогда, когда приемный тракт является линейным по отношению к входным сигналам. В реальных же условиях, тем более с учетом радиолокационного противодействия, это может быть совсем не так. И тут уже появляется принципиальная разница в поведении ДОС у активной и пассивной решетки, и эта разница совсем не в пользу АФАР. Приступаем к эксперименту.

Ставим помеху, или как jamming портит красивую картинку

Эксперимент будет проходить следующим образом. Устанавливаем источник помехи и будем наблюдать, какое воздействие это возымет на АФАР и ПФАР. Требования к помехе не сильно жесткие: достаточно, чтобы ее мощность создавала достаточный уровень в динамическом диапазоне T/R модулей; тип модуляции не принципиален, частота тоже: достаточно чтобы она была в достаточно широкой полосе пропускания антенных систем. Расположение источника помехи тоже некритично: достаточно лишь, чтобы он находился в диапазоне углового сканирования радиолокатора.

На этом этапе, вслед за программистами уходят пить кофе менеджеры и журналисты. Ведь они прекрасно знают, что для этого и формируется диаграмма направленности, чтобы подавлять подобные помехи. Более того, АФАР прекрасно может запеленговать источник и сформировать диаграмму таким образом, чтобы в направлении помежи был нулевой уровень диаграммы. Поэтому помеха будет подавлена провалом в сформированной диаграмме направленности. Тоже на первый взгляд.

Да, это действительно так, но… опять таки справедливо только тогда, когда тракт приема является линейным. Если помеха является блокирующей, то есть нарушающей линейность тракта, то благостная картинка просто перестает существовать. В ГОСТах, которые формируют требования к профессиональной радиоприемной технике, этот параметр так и называется — блокирование, который определяется как «изменение отклика на полезный радиосигнал при наличии на входе радиоприемного устройства хотя бы одной радиопомехи». Заметьте: в линейной системе сигнал и помеха суммируются независимо друг от друга, поэтому отклик на полезный сигнал не меняется в зависимости от помехи. Это может произойти только в том случае, если помеха нарушает линейность приемного тракта, в нашем случае — приемной цепи T/R модуля, состоящей из LNA и АЦП.

В ПФАР диаграммообразующая система (ДОС) находится до T/R модуля, поэтому последний защищен от блокирующей помехи диаграммой направленности. В идеальном варианте, как было подмечено выше, если источник помехи попадает в ноль диаграммы и на входе T/R модуля она будет равна нулю. Это принципиальный момент, на который я обращаю ваше внимание.

В АФАР ДОС находится после T/R модулей, вследствие чего они беззащитны перед сигналами на своих входах. Минимумы диаграммы подавляющие помехи будут вычисляться также после этих модулей, и поэтому если они будут блокированы, то полезный сигнал на выходе этих модулей просто не попадет — никакие фильтры не помогут.

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

Часто можно привести цитату, в которой проблема сформулирована лучше, чем ее можешь сформулировать ты. Сейчас как раз такой случай, поэтому процитирую первоисточник. Он как раз указывает на проблему, присущую радиоприемным устройствам (РПУ) которые используют прямую оцифровку антенного входа с помощью высокочастотных АЦП:

Основным недостатком, ограничивающим широкое применение подобного типа РПУ, является невозможность обеспечения высоких уровней динамического диапазона по блокированию. Поскольку на входе АЦП отсутствует фильтрация, то АЦП соответственно не может выполнять оцифровку одновременно сигналов большого и малого уровня. В соответствие нормам ГОСТ радиоприемник должен обеспечивать уровень восприимчивости по блокированию не менее 126 дБ/мкВ для РПУ второго класса и 130 дБ/мкВ для РПУ первого класса. Очевидно, что существующие типы АЦП 14-16 бит не в состоянии обеспечить выполнение этого параметра.

Вот и все. АЦП принципиально напрочь убивает динамический диапазон, что создает уязвимость АФАР для постановки блокирующей помехи. Осталось только оценить степень ее реализуемости. Для этого нам надо оценить чувствительность T/R модуля в локаторе F-35, чтобы на основе этого значения рассчитать потребную мощность помехи и на каком расстоянии может располагаться ее источник.

Если вы найдете ошибку в нижеследующих расчетах, не стесняйтесь! Смело пишите замечания и предложения в комменты )

Энергетический бюджет радиолинии локатор -> цель -> локатор

Нас интересуют две величины:

  • мощность отраженного от цели сигнала, для оценки потребной мощности джаммера;
  • уровень сигнала на входе антенной системы локатора, для оценки наступления момента блокирования АЦП TRM.

В качестве исходных данных возьмем значения:

Pt = 20kW: мощность передатчика радиолокатора F-35;

λ = 3cm: длина волны для частоты 10ГГц;

Gt = 35dB: коэффициент усиления антенны радиолокатора на передачу, линейное значение: 3162;

R = 400km: дальность до цели;

σ = 3m2 : ЭПР цели.

Фактически, при построении бюджета мы поэтапно воспроизводим основное уравнение радиолокации. Причина, по которой мы его не используем — это необходимость в промежуточных данных и соответственно прозрачность в том, как они получены.

Начнем с самого начала — передатчика. Изотропный излучатель мощностью Pt создаст плотность мощности D на расстоянии R:

\displaystyle\large D=\frac{P_t}{4{\pi}R^{2}} [1]

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

Чтобы ее повысить, используем направленные свойства антенны с коэффициентом усиления Gt, тогда плотность излучаемой мощности [Вт/м2] составит:

\displaystyle\large D=\frac{P_tG_t}{4{\pi}R^{2}} [2]

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

При облучении цели нашим передатчиком мы будем принимать мощность, зависящую от геометрических размеров цели. Если цель для простоты изложения будем представлять как антенну, тогда принимаемая целью мощность Pr будет зависеть от эффективной площади этой антенны Ae:

\displaystyle\large P_r={DA_e}=\frac{P_tG_tA_e}{4{\pi}R^2} [3]

В свою очередь, эффективная площадь напрямую связана с коэффициентом усиления приемной антенны (читай — цели):

\displaystyle\large A_e=\frac{G_r\lambda^{2}}{4{\pi}} [4]

Выполняя очередную подстановку из [4] в [3], получим значение для уровня принимаемого сигнала в простой радиолинии передающая антенна локатора -> цель:

\displaystyle\large P_r=\frac{P_tG_tG_r\lambda^{2}}{(4{\pi)}^2R^{2}} [5]

Пока все понятно с точки зрения физики за исключением того, как интерпретировать коэффициент усиления цели Gr.

Но мы не будем ломать над этим голову, поскольку этот параметр напрямую связан с показателем ЭПР цели σ соотношением

\displaystyle\large G_r=\frac{4{\pi} \sigma}{\lambda ^2} [6]

Если мы подставим это в предыдущее выражение [5], то получим значение мощности сигнала переотраженного от цели:

\displaystyle\large P_{t2}=\frac{P_tG_t \sigma}{4{\pi}R^2} [7]

Индекс t2 теперь означает, что теперь это другое значение мощности передачи, на этот раз с какой интенсивностью цель светит обратно на локатор.

Теперь распространение пошло в обратном направлении — от цели к приемной антенне локатора, и для радиолинии цель -> приемная антенна локатора мы будем использовать точно такое же выражение [5]:

\displaystyle\large P_{r2}=\frac{P_{t3}G_r G_{r2}\lambda^{2}}{(4{\pi)}^2R^{2}} [8]

Новые обозначения несут следующий смысл:

Pr2: мощность сигнала, принимаемого антенной радиолокатора;

Gr2: коэффициент усиления антенны радиолокатора на прием: очевидно, что он будет таким же, как и Gt. Позже мы вернемся к этому;

Pt3*Gr: мощность отраженного целью сигнала, которая на самом деле есть не что иное, как Pt2.

Подставляя значение Pt2 из [7] вместо произведения Pt3*Gr в [8], получаем:

\displaystyle\large P_{r2}=\frac{P_{t2} G_{r2}\lambda^{2}}{(4{\pi)}^2R^{2}} [9]

\displaystyle\large P_{r2}=\frac{P_tG_t \sigma}{4{\pi}R^2} \cdot \frac{G_{r2}\lambda^{2}}{(4{\pi)}^2R^{2}} [10]

и в результате мощность, принимаемая на выходе антенны радиолокатора, будет равна

\displaystyle\large P_{r2}=\frac{P_t G_t G_{r2}\lambda^{2} \sigma}{(4{\pi)}^3R^{4}} [11]

Я не случайно сделал оговорку: на выходе антенны, поскольку выход — это уже суммирование сигналов T/R элементов, а нас интересует как раз мощность на их входе. Но обо всем по порядку: каркас из формул мы создали, теперь начинаем считать.

Немного о смыслах: физических

Итоговые значения можно схематически представить в следующем виде:

F-35, АФАР, ПФАР, AESA, PESA, Radar, Радиолокатор, Локатор, Antenna  Pattern, блокирование, обнаружение, мощность, TRM, T/R module, Transmit Receive Module

Передаваемые и принимаемые мощности в бюджете радиолинии локатора

Мы можем сразу посчитать порядок значений мощности сигнала отраженного от цели Pt2 [7] для определения сопоставимой мощности, требуемой от источника помехи — джаммера. Что касается мощности Pr2, то это суммарная мощность, образуемая вкладом всех TRM антенны. Собственно в этом физический смысл коэффициента усиления антенны G: собирая сигналы со всех T/R модулей, повышается энергетическая эффективность в направлении синфазного сложения сигналов, то есть в направлении основного лепестка диаграммы. При этом несущественно, является ли антенна дискретной решеткой, как в нашем случае, или представляет собой непрерывную конструкцию, как например рефлекторы спутниковых антенн: все равно, или с физической, или математической точки зрения происходит суммирование (а точнее интегрирование) мощностей, принимаемых парциальными элементами площади рефлектора.

В результате, чтобы оценить мощность сигнала принимаемого каждым T/R модулем, нужно грубо говоря разделить выходную мощность антенны Pr2 на количество модулей, а точнее — убрать значение коэффициента усиления G из формулы [11]. Для сомневающихся предлагаю провести самостоятельный эксперимент: построить диаграмму направленности такой антенны из 1676 модулей и сопоставить площадь основного лепестка с уровнем побочных излучений. Получится тоже самое значение 35 дБ. Собственно говоря, это значение было так и получено.

Тогда уровень сигнала, отраженного от цели на входе TRM будет

\displaystyle\large P_{trm}=\frac{P_t G_t\lambda^{2} \sigma}{(4{\pi)}^3R^{4}} [12]

Вы заметили, как резко снизился порог воздействия на вход TRM — на целых G=35 дБ? Это то, о чем я говорил в начале статьи об особенностях АФАР.

Считаем

Наконец, после долгой возни с формулами, переходим к числам ). Подставляя исходные значения в [7] и [12], получаем:

мощность сигнала отраженного от цели Pt2=-10.2 dBm;

мощность сигнала на входе TRM Ptrm= -144.7 dBm.

Как чувствует себя локатор F-35 с такой не очень маленькой целью (ЭПР 3м2 это небольших размеров самолет) на расстоянии 400км? Абсолютное значение сигнала -144.7 dBm на входе TRM нужно соотнести с уровнем шума, чтобы делать выводы по отношению сигнал/шум. Для этого воспользуемся материалами по потерям в антенно-фидерном тракте бортового радиолокатора AN/APG-81 установленного на F-35.

По приведенным данным, с учетом потерь на распространение шумовая температура всего приемного тракта составит 996К. Это значение получено путем суммирования значений шумовой температуры антенны с учетом атмосферного шума (96К) и потерь в приемном фидере (98К). Шумовая температура приемника 608К получена из комнатной температуры (290К) с учетом коэффициента шума 4,91 дБ — столько вносит собственно приемник, или LNA входящий в состав TRM. С учетом потерь в приемном тракте 1,31 получается искомое значение 996К.

Умножая шумовую температуру на постоянную Больцмана, получаем шумовую плотность мощности, приведенную ко входу приемника 1.374*10-20 Вт/Гц. Это относительная величина, которая показывает мощность шума на единицу ширины спектра. Чтобы получить абсолютное значение шума — шумовую мощность, необходимо учесть ширину полосы принимаемого сигнала. С длительностью зондирующего имульса 1 мкс требуемая полоса пропускания составит около 1 МГц; таким образом будем ориентироваться на шумовую мощность 1.374*10-20 Вт/Гц * 106 Гц =  1.374*10-14 Вт, или -108.62 дБм (мощность в дБ относительно значения 1 мВт). Много это или мало? Предположим, что эта мощность приложена к стандартной нагрузке 50 Ом, тогда шумовое напряжение на входе приемника составит около 0.83 мкВ. Вполне реальное значение.

Таким образом, шумовое значение на входе T/R модуля радиолокатора AN-APG-81 составит минус 109 дБм. Отношение сигнал/шум составит -144.7 dBm — 109 dBm = -35.7 dB. Не очень здорово, скажем так, но не будем забывать что позади еще схема диаграммообразования антенны, в которой сигнал основного лепестка сложится синфазно, шум сложится кое-как, и в результате отношение сигнал/шум на выходе антенны будет -35.7 dB + 35 dB = -0.7 dB, что уже не так плохо.

Подчеркнем еще раз: при воздействии на вход T/R модуля радиолокатор с АФАР теряет преимущество в использовании коэффициента усиления антенны, что ни много ни мало 35 дБ.

Динамический диапазон 12-битного АЦП в составе TRM составляет 72.2 дБ. Предположим что коэффициент передачи усилителя LNA перед АЦП равен единице, поскольку его значение не влияет на физику блокирования АЦП — меняется только масштаб величин. Естественно, что АЦП будет работать при существенно более значимых входных сигналах, но для нас имеет значение только относительный расчет. Тогда диапазон входных сигналов, которые полностью займут разрядную сетку АЦП, составит от -144.7 dBm до -144.7 dBm + 72.2 dB =  -72.5 dBm.

Чтобы стать самым сильным сигналом для АЦП (а это уже почти блокирование), постановщику помехи — джаммеру достаточно иметь мощность -10.2 dBm + 72.2 dB = 62 dBm, или 1.58 кВт. Таким образом, при мощности джаммера 1.58 кВт дальности 400км у радиолокатора F-35 оказывается под вопросом.

На самом деле, картинка несколько сложнее, поскольку мы имеем дело с импульсными сигналами, а значит они не обязательно должны перекрываться во времени. Если джаммер будет работать с duty circle 100%, то подавление будет полным, но этот режим требует больших энергоресурсов джаммера и не может использоваться в течение длительного времени.

Сделаем оценку того, какое воздействие окажет мощность помехи например 5 кВт, или 67 dBm. В этом случае эквивалентная минимальная величина отраженного от цели сигнала составит 67 dBm — 72.2 dB = -5.2 dBm. Такое значение соответствует дальности локатора около 225 км, при этом помеха точно также как и в предыдущем случае займет весь динамический диапазон АЦП, а полезный отраженный сигнал будет находиться на уровне одного бита.

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

Заключение

В статье было исследовано влияние метода прямой оцифровки сигнала с антенного модуля АФАР радиолокатора AN/APG-81 истребителя-бомбардировщика F-35 и связанная с этим возможность блокирования АЦП модуля T/R. Получены оценки потребной мощности постановщика помех (джаммера), который находится на таком же расстоянии, как и цель локатора.

Значение пораженной дальности локатора AN/APG-81 составит 400км при мощности джаммера около 1.58 кВт и 225км при мощности джаммера около 5 кВт. При таком блокировании радиолокатор F-35 теряет возможности формирования диаграммы и подавления джаммера.

Данные приведены при непрерывном излучении джаммера, в импульсном режиме, когда отраженные от цели сигналы и помеха могут не перекрываться, степень подавления уменьшится.

Расчет произведен для 12-разрядных АЦП приемных модулей. С повышением разрядности эффективность подавления также уменьшится. Ее величину можно оценить пользуясь приведенным математическим аппаратом.

Блеск и горечь архитектуры OMAP 3530

Для встраиваемых систем выбор кубиков, которые можно сложить во что-нибудь путное, не так уж и велик. То, что находится прямо перед глазами, не дает однозначного выбора.

ARM хорош и к тому же быстро развивается, сопроцессор с плавающей точкой NEON с распараллеливанием выполнения (pipeline) уже само собой разумеющийся атрибут. В последнее время обозначилась новая мода — использовать GPU для решения вычислительных задач, таких как FFT. Программировать под ARM — одно удовольствие, если пользоваться кросс-компилятором на собственной машине, среда Embedded Linux как правило привычна и дружественна разработчику. За все это заплачено отсутствием реального времени и возможностей обработки данных в синхронном режиме. Что же, для этого есть другие инструменты.

FPGA мыслится как первое что можно поставить скажем после АЦП. Синхронная обработка параллельных данных и возможность фильтрации на высокой частоте выборок (до 500 MS/s) определяют ПЛИС как стандарт де-факто на первом месте в сигнальном тракте. После фильтрации и децимации сигнал получается использовать как в DSP, так и в том же ARM. Конечно, с ПЛИС лучше работать не программисту (который может получить вывих мозга), а традиционному инженеру-схемотехнику. В конечном счете, FPGA есть не что иное как миллионы логических элементов, которые нужно просто соединить правильным образом.

Роль DSP также понятна: числодробилка с собственным pipeline, которая к тому же может принимать данные с синхронного интерфейса и вести обработку на несущей частоте. В современных процессорах операция FFT над выборкой 1К чисел с плавающей точкой занимает буквально несколько микросекунд. Сколько гребенок доплеровского фильтра можно сделать с таким процессором!

Рано или поздно кому-нибудь пришла бы в голову мысль скрестить между собой попарно эти кубики. В результате родилась архитектура Zynq, где возник симбиоз ARM + FPGA, и архитектура OMAP, основанная на ARM + DSP. Вот про вторую конфигурацию я бы хотел поговорить подробнее.

ARM+DSP или DSP+ARM?

Идея работать в дружественной среде Linux и одновременно получить те возможности, которые дает DSP, выглядит очень привлекательно. Для радиоинженера логичной бы выглядела связка DSP+ARM (именно в этой последовательности), в которой сигнал через физический интерфейс поступает в обработку DSP, в результате которой извлекаются информационные параметры сигнала, и ARM принимая эти полученные данные доделывает уже остальное: организует третичную обработку, отображение, управление и контроль.

Однако, все получилось немного не так. По замыслу разработчиков архитектуры OMAP, которая позиционируется как мультимедийная, DSP выступает в роли Accelerator Subsystem, фактически как сопроцессор-акселератор на подхвате у ARM. В принципе понятно, для чего это придумывалось: ARM получает данные с какой нибудь камеры, а DSP сопроцессор берет на себя критичные куски алгоритма, для которых требуется производительность. Но мы не привередливые: пусть будет ARM+DSP, где Linux на первой руке получает блоки данных, пусть и в асинхронном режиме, а условный доплеровский фильтр берет на себя DSP. На том и порешили, после двинемся дальше.

Codec Engine

Поскольку мы имеем представление, что будут делать ARM и DSP порознь, сосредоточимся на том, как они будут обмениваться данными. Хотя эти ядра и расположены на одном кристалле, но все таки это два совершенно разных процессора. Интуитивно понятно, как это может быть сделано: для этих процессоров существует некая общая область памяти, которую заполняет ARM и читает DSP и наоборот, если мы хотим сделать обмен данными двунаправленным. Ну и конечно должны быть библиотеки неких функций, которые можно дергать со стороны ARM, что проявится в виде неких сигналов в подсистеме DSP: данные мол готовы, забирайте.

Для тех, кто сталкивался с обеспечением подобного рода взаимодействия, а точнее — с разработкой протокола, известно с какой головной болью это связано. Поэтому благой вестью для нас является то, что компания Texas Instruments, разработчик SoC OMAP 3530, очень сильно постаралась не только для создания такого протокола, но пошла еще дальше — создала целый программный API под названием Codec Engine. Вот как эта структура выглядит на практике:

OMAP 3530, ARM, DSP, Codec Engine

Codec Engine: передача данных между ARM и DSP

Codec Engine, или CE, берет на себя множество мелочей, в которых так легко сделать ошибку: выделение памяти под передаваемые данные как со стороны ARM, так и со стороны DSP, поддержку функций обмена, средства сборки проекта опять таки со стороны ARM Linux, так и со стороны SYS/BIOS DSP. Кроме этого библиотеки CE включают в себя средства отладки, что вообще замечательно в части DSP, потому как просто так в этот процессор не залезешь.

Механизм обмена между ядрами ARM и DSP я использовал в проекте пассивного радиолокатора, где DSP подсистема использовалась для тяжелонагруженного алгоритма нахождения функции неопределенности сигнала.

Ложка дегтя

Так и стали бы ARM с DSP жить и добра наживать, и потускнел бы заголовок нашей статьи — причем здесь горечь, видны только одни радости? Как обычно бывает, при погружении в каждую технологию начинают всплывать вещи, о которых вначале не подозреваешь. Ничего страшного конечно не происходит, но есть нюанс. Об этой ложке дегтя — далее.

Сам CE — достаточно тяжелое решение. То, что компилируется из исходников, у меня на машине состоит из 103599 файлов — ни много ни мало. Собственные приложения выглядят карликом на фоне этого монстра ) Проект был собран и запущен в работу, и все было ОК. Но как-то появилась необходимость опробовать новые приложения на ARM стороне, которые требовали версии ядра Linux постарше чем 2.16. В чем проблема, накатим новое ядро! И тут опс… Codec Engine не захотел собираться с новым ядром. В чем причина?

Как выяснилось, подсистема Линукс-ядра IOMMU, которая отвечает за отображение памяти на периферийные устройства и которую использует CE, существенно изменилась в версиях Linux старше 3.0.50. Почему именно этот номер версии — потому что это максимум, чего получилось достичь вместе с патчами ядра. Вот тут и  обозначился проблемный аспект архитектуры OMAP: на апгрейд Linux на стороне ARM можно ставить крест. Нет, вы конечно можете использовать только ARM ядро, без DSP с любой версией Linux. Но тогда про DSP можно забыть.

Второе. Само собой разумеется, моим приложениям на ARM понадобилась поддержка операций с плавающей точкой, и я решил поставить дистрибутив с аппаратной поддержкой этих операций. И соответственно перекомпилировать CE с новыми библиотеками.  И тут началось… как выяснилось Texas Instruments как бы это мягче сказать… не вполне наделил свои исходники возможностями брать конфигурацию с корневых make файлов. Я обнаружил кучу кода, который отказывался компилироваться просто по той причине, что он просто игнорировал замену конфигурации с arm-linux-gnueabi на arm-linux-gnueabihf. Приходилось править его вручную. Было заметно, что в дебрях ветвей CE находятся такие древние файлы, что похоже в TI могло не остаться людей которые могут подсказать что они там делают )

Так что будьте осторожны с такими комплексными архитектурами. После экспериментов со связкой ARM+DSP невольно стала напрашиваться мысль что лучше было бы сделать взаимодействие между ними на аппаратном уровне…

Что касается Zynq, то обмен между ARM и FPGA был выполнен на удивление просто: стандартными средствами Linux DMA через обычное устройство /dev/xdma. Простая поддержка файловых операций, не более того. Интересно, почему по этому пути не пошли Texas Instruments в Codec Engine?