MAVLink: сериализация пакета Печать
Добавил(а) microsin   

В этой статье (перевод документации [1]) предоставляется подробная информация о сериализации пакета протокола MAVLink, включая физические форматы передаваемых данных для пакетов MAVLink версий v1 и v2, упорядочивание полей в полезной нагрузке сообщения, а также контрольной суммы CRC_EXTRA, используемой для гарантии, что передатчик и приемник использую совместимое определение сообщения.

Примечание: приведенная здесь информация главным образом предназначена для разработчиков, кто создает или поддерживает MAVLink generator (см. врезку "Генерация библиотек MAVLink" статьи [2]. Обычным пользователям MAVLink обычно не нужно разбираться в тонкостях сериализации формата, потому что кодирование/декодирование обрабатывается библиотеками MAVLink (от пользователя нужно только подготовить корректное описание протокола в XML-формате, и сгенерировать на основе его код библиотек). Для объяснение непонятных терминов и сокращений см. раздел "Словарик MAVLink" в конце статьи [2].

[Формат пакета]

Формат пакета MAVLink создавался по образцу стандартов CAN и SAE AS-4 [3]. Обратите внимание, что многобайтные поля сериализованы в формате little-endian [4], и библиотеки MAVLink по умолчанию сконфигурированы для работы на железе с архитектурой чисел little-endian.

Формат пакета MAVLink 2. Ниже показан формат пакета MAVLink 2 (реальное представление данных в памяти может отличаться).

packet mavlink v2

Индекс байта C-версия Содержимое Значение Объяснение
0 uint8_t magic Маркер начала пакета 0xFD Специфичный для протокола маркер начала текста (start-of-text, STX), который используется для обозначение начала нового пакета. Любая система, которая не понимает эту версию протокола, пропустит пакет с этим маркером.
1 uint8_t len Длина полезной нагрузки 0 - 255 Показывает количество байт в последующей секции полезной нагрузки. На это может повлиять обрезка полезной нагрузки (payload truncation).
2 uint8_t incompat_flags Флаги несовместимости   Флаги, которые обработчик протокола обязан понимать, чтобы обеспечить совместимость для MAVLink (реализация отбросит пакет, если она не понимает флаг).
3 uint8_t compat_flags Флаги совместимости   Флаги, которые могут быть игнорированы, если их не понимает обработчик протокола (реализация все еще может обработать пакет, даже если она не поняла какой-то флаг).
4 uint8_t seq Последовательный номер пакета 0 - 255 Это поле используется для детектирования потери пакета. Компоненты инкрементируют это значение для каждого отправляемого сообщения.
5 uint8_t sysid System ID (отправитель) 1 - 255 ID системы (транспортное средство, vehicle), отправившей сообщение. Используется для дифференцирования систем в сети. Обратите внимание, что широковещательный (broadcast) адрес 0 нельзя использовать в этом поле, поскольку он является недопустимым адресом источника.
6 uint8_t compid Component ID (отправитель) 1 - 255 ID компонента, отправившего сообщение. Используется для дифференцирования компонентов в системе (например, так можно отличить автопилот и камеру). Используйте для этого поля подходящие значения в MAV_COMPONENT. Обратите внимание, что широковещательный адрес MAV_COMP_ID_ALL нельзя использовать в этом поле, потому что это недопустимый адрес источника.
7 .. 9 uint32_t msgid:24 Message ID (младший, средний, старший байты) 0 - 16777215 ID типа сообщения в полезной нагрузке. Используется для декодирования данных, находящихся в объекте сообщения.
Для n-байтной полезной нагрузки: n=0: NA, n=1: 10, n>=2: 10 .. (9+n) uint8_t payload[max 255] Полезная нагрузка   Данные сообщения, зависящие от типа сообщения (т. е. от Message ID) и содержимого сообщения.
(n+10) .. (n+11) uint16_t checksum Контрольная сумма (младший байт, старший байт)   CRC-16/MCRF4XX для сообщения, исключая байт magic. Включает байт CRC_EXTRA.
(n+12) .. (n+25) uint8_t signature[13] Подпись   (опционально) цифровая подпись, обеспечивающая защиту данных от подмены.

Замечания:

• Минимальная длина пакета составляет 12 байт для пакетов подтверждения (acknowledgment), у которых нет полезной нагрузки.
• Максимальная длина пакета составляет 280 байт для подписанного сообщения, использующего всю полезную нагрузку.

Формат пакета MAVLink 1. Ниже показан формат пакета MAVLink 1 (реальное представление данных в памяти может отличаться).

packet mavlink v1

Индекс байта C-версия Содержимое Значение Объяснение
0 uint8_t magic Маркер начала пакета 0xFE Специфичный для протокола маркер начала текста (start-of-text, STX), который используется для обозначение начала нового пакета. Любая система, которая не понимает эту версию протокола, пропустит пакет с этим маркером.
1 uint8_t len Длина полезной нагрузки 0 - 255 Показывает количество байт в последующей секции полезной нагрузки. На это может повлиять обрезка полезной нагрузки (payload truncation).
2 uint8_t seq Последовательный номер пакета 0 - 255 Это поле используется для детектирования потери пакета. Компоненты инкрементируют это значение для каждого отправляемого сообщения.
3 uint8_t sysid System ID (отправитель) 1 - 255 ID системы (транспортное средство, vehicle), отправившей сообщение. Используется для дифференцирования систем в сети. Обратите внимание, что широковещательный (broadcast) адрес 0 нельзя использовать в этом поле, поскольку он является недопустимым адресом источника.
4 uint8_t compid Component ID (отправитель) 1 - 255 ID компонента, отправившего сообщение. Используется для дифференцирования компонентов в системе (например, так можно отличить автопилот и камеру). Используйте для этого поля подходящие значения в MAV_COMPONENT. Обратите внимание, что широковещательный адрес MAV_COMP_ID_ALL нельзя использовать в этом поле, потому что это недопустимый адрес источника.
5 uint8_t msgid Message ID 0 - 255 ID типа сообщения в полезной нагрузке. Используется для декодирования данных, находящихся в объекте сообщения.
Для n-байтной полезной нагрузки: n=0: NA, n=1: 6, n>=2: 6 .. (5+n) uint8_t payload[max 255] Полезная нагрузка   Данные сообщения, зависящие от типа сообщения (т. е. от Message ID) и содержимого сообщения.
(n+6) .. (n+7) uint16_t checksum Контрольная сумма (младший байт, старший байт)   CRC-16/MCRF4XX для сообщения, исключая байт magic. Включает байт CRC_EXTRA.

Замечания:

• Минимальная длина пакета составляет 8 байт для пакетов подтверждения (acknowledgment), у которых нет полезной нагрузки.
• Максимальная длина пакета составляет 263 байт для сообщения, использующего всю полезную нагрузку.

Флаги несовместимости (MAVLink 2). Эти флаги используются для того, чтобы показать фичи, которые библиотека MAVLink должна поддерживать для обеспечения возможности обработки пакета. Это включает любую фичу, которая влияет на формат/упорядочивание пакета. Реализация MAVLink должна отбросить пакет, если она не понимает любой флаг, установленный в поле incompat_flags.

Поддерживаемые флаги несовместимости в настоящий момент:

Флаг Имя C Функция
0x01 MAVLINK_IFLAG_SIGNED Пакет подписан (к пакету присоединена цифровая подпись).

Флаги совместимости (MAVLink 2). Эти флаги используются для указания, какие фичи не помешают библиотеке MAVLink обрабатывать пакет (даже если фича не понятна). Это может включать в себя, например флаг, указывающий, что пакет должен обрабатываться как "высокоприоритетный" (такие сообщения могут обрабатываться любой реализацией MAVLink, поскольку на формат и структуру пакета это не влияет). Реализация MAVLink может безопасно игнорировать флаги, которые она не понимает, когда они установлены в поле compat_flags.

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

Сообщение кодируются в пакете MAVLink следующими полями:

msgid (идентификатор сообщения) это поле идентифицирует, как сообщение закодировано в пакете.

payload (полезная нагрузка) это поле содержит данные сообщения.

- MAVLink переупорядочивает поля сообщений в полезной нагрузке для передачи по физическому каналу связи (из порядка в исходных определениях XML-сообщений).
- MAVLink 2 обрезает (см. далее "Empty-Byte Payload Truncation") любые нулевые байты в конце полезной нагрузки перед тем, как сообщение будет отправлено, путем соответствующей установки поля len пакета (MAVLink 1 всегда посылает все байты).

len содержит длину данных (количество байт) полезной нагрузки.

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

Совет: библиотека MAVLink должна уведомлять об ошибке CRC во время декодирования, если спецификация сообщения несовместима (например, C-библиотека mavlink_parse_char() выдает в этом случае статус MAVLINK_FRAMING_BAD_CRC).

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

• Поля сортируются по их традиционному типу данных:

        (u)int64_t, double (8 байт)
        (u)int32_t, float (4 байта)
        (u)int16_t (2 байта)
        (u)int8_t, char (1 байт)

• Если два поля имеют одинаковую длину, их порядок сохраняется в том виде, в котором он существовал до упорядочения размера поля данных.

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

• Порядок передачи по воздуху тот же, что и для структуры, и, таким образом, представляет переупорядоченные поля.

• Поле CRC_EXTRA вычисляется поле переупорядочивания для гарантии, что ошибка во время переупорядочивания будет обнаружена несовпадением CRC. Предоставленные образцовые реализации на языках Python, C и C# проверяются на правильность переупорядочивания полей, это касается только пользовательских реализаций.

Исключением для упомянутого выше переупорядочивания являются только поля расширения MAVLink 2 (extension fields). Extension fields отправляются в том порядке, в каком они присутствуют в XML-декларации, и они не включают вычисление CRC_EXTRA. Это позволяет новым extension fields быть добавленными в конец сообщения без нарушения двоичной совместимости.

ПРЕДУПРЕЖДЕНИЕ: это упорядочивание уникально, и может быть просто реализовано в генераторе протокола с помощью стабильного алгоритма сортировки. Альтернативой использованию сортировки было бы либо использование не эффективного выравнивания, что плохо для целевых архитектур типичных приложений MAVLink, либо иметь вызовы функций в порядке размера переменной вместо контекста приложения. Это привело бы к очень запутанным сигнатурам функций сериализации.

Empty-Byte Payload Truncation (MAVLink 2). Реализации MAVLink 2 должны вырезать любые пустые (нулевые) байты в конце сериализованной полезной нагрузки перед её передачей. Это поведение отличается от MAVLink 1, где байты посылаются для всех полей полностью, независимо от их содержимого.

Реализация, которая принимает (несовместимое) сообщение MAVLink 2 с нулевыми завершающими байтами, все еще должно поддерживать декодирование сообщение (если оно в противном случае является действительным), и обеспечивать методы маршрутизации/пересылки сообщений. Сообщение может быть переслано либо полностью не измененным (т. е. с не обрезанными нулями и оригинальной CRC), либо реализация пересылки может обрезать нули и заново вычислить CRC.

Фактически затронутые поля / сохраненные байты зависят от сообщения и его содержимого: переупорядочивание полей MAVLink означает, что все что мы можем сказать, так это то, что любые усеченные поля обычно будут полями с наименьшим размером данных или полями расширения (extension fields).

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

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

СОВЕТ: изменения в спецификациях сообщения, которые могут сделать несовместимым формат передаваемых по каналу связи данных, включают: новые/удаленные поля, либо изменения имени поля, типа данных, их порядка следования или длины массива.

Когда запускается генератор кода библиотеки MAVLink, он берет контрольную сумму структуры XML для каждого сообщения, и создает массив определения MAVLINK_MESSAGE_CRCS. Это используется для инициализации массива mavlink_message_crcs[] в реализации C/C++, и подобным образом используется в реализации Python (или любой другой реализации, такой как C# и JavaScript).

Когда передатчик вычисляет контрольную сумму для сообщения, он добавляет байт CRC_EXTRA в конец данных, по которым вычисляется контрольная сумма. Получатель вычисляет контрольную сумму для принятого сообщения и добавляет свой CRC_EXTRA для определенного идентификатора сообщения (message id). Если CRC_EXTRA для отправителя и получателя отличаются, то контрольные суммы передатчика и получателя не совпадут.

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

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

Поскольку MAVLink внутри себя переупорядочивает поля сообщения по их размеру, чтобы не допустить проблем выравнивания слов и половин слов (см. в Википедии описание выравнивания данных в структуре), неправильно реализованное переупорядочивание может также потенциально вызвать несоответствия, CRC_EXTRA вычисляется на основе компоновки сообщения, передаваемого по воздуху, а не не основе порядка в XML.

Замечание: MAVLink 2 extension fields не включаются в вычисление CRC_EXTRA.

Следующий код Python вычисляет CRC_EXTRA seed:

def message_checksum(msg):
    '''Вычисление 8-битной контрольной суммы от ключевых полей
       сообщения, таким способом мы можем обнаружить несовместимые
       изменения XML'''
    from .mavcrc import x25crc
    crc = x25crc()
    crc.accumulate_str(msg.name + ' ')
    # Для того, чтобы могли быть присоединены расширения, crc
    # не включает любые поля расширений (field extensions)
    crc_end = msg.base_fields()
    for i in range(crc_end):
        f = msg.ordered_fields[i]
        crc.accumulate_str(f.type + ' ')
        crc.accumulate_str(f.name + ' ')
        if f.array_length:
            crc.accumulate([f.array_length])
    return (crc.crc&0xFF) ^ (crc.crc>>8)

Замечание: это использует тот же самый алгоритм CRC-16/MCRF4XX, который применяется runtime. Он вычисляет CRC от имени сообщения (такого как "RAW_IMU"), за которым идет тип и имя каждого поля, отделенные пробелом. Порядок следования полей такой, как они посылаются по каналу связи. Для массивов также добавляется длина массива.

Контрольная сумма. Формат пакета включает 2-байтную CRC-16/MCRF4XX, которая позволяет обнаружить повреждение данных сообщения. См. исходный код MAVLink для документированной C-реализации.

CRC покрывает все сообщение целиком, исключая magic-байт и цифровую подпись в конце (если она имеется). CRC включает байт CRC_EXTRA, который используется для гарантии, что системы передатчика и приемника имеют общее представление об определении сообщения.

[Ссылки]

1. MAVLink Packet Serialization site:mavlink.io.
2. MAVLink: руководство разработчика.
3. AS-4 Unmanned Systems Committee site:sae.org.
4. Порядок следования байт (endianness).
5. MAVLink-Standard Definitions site:mavlink.io.