Saturday, April 6, 2013

MAVLink Protocol Notes: Packet Decoding

Here's how to decode a MAVLink Packet.  Some sample code here, and a video of the multicast demo here.





Example Packet (Heartbeat)
packet length = 17 (6 bytes header + 9 bytes payload + 2 bytes checksum)

hdr len seq sys cmp id   payload (len 9)------------------  crc---
 0   1   2   3   4   5   6   7   8   9   a   b   c   d   e   f  10
FE  09  4E  01  01  00  00  00  00  00  02  03  51  04  03  1C  7F

Fixed Header (6 octets)
0. packet header, always 0xFE
1. message length (9)
2. sequence number -- rolls around from 255 to 0 (0x4e, previous was 0x4d)
3. source system - what system is sending this message (1)
4. source component - what component of the system is sending the message (1)
5. message ID (0 = heartbeat)

Variable Sized Payload (specified in octet 1, range 0..255)
06-0e. payload

Packet Checksum
0f-10. CRC25

In common use (single aircraft, single autopilot), source component and systems will be (1,1)



Example Command and Payload

Command (octet 5) = 0, Heartbeat

Parms (octets 06-0e):
06-09. custom_mode = 00
0a. mav type = 2 (quadrotor)
0b. autopilot = 3 (ardupilot mega)
0c. base_mode = 0x51 = 0b01010001 (manual input, stabilized, custom mode)
0d. system_status = 4 (active, motors engaged)
0e. self.mavlink_version = 3

The parameter ordering can be confusing if you look at the XML specification, since for efficiency the parameters are not sent in the order they are listed, but are reordered as specified here.

type          uint8_t     Type of the MAV
autopilot     uint8_t     Autopilot type / class
base_mode     uint8_t     System mode bitfield
custom_mode   uint32_t    Autopilot-specific bitfield
system_status uint8_t     System status flag
mavlink_version uint8_t_mavlink_version      MAVLink version


(update: I didn't find this page which covers this material)

Checksum

The last two octets are the packet checksum. The first octet of the packet (the 0xFE header) is not included in the checksum.

In addition, a magic character based on the message type is included in the checksum.After the packet (less the header) has been shifted through the CRC accumulator, shift the one-byte magic number through the CRC accumulator to get the final checksum for the packet.  I'll put a pointer to some sample code once I get it uploaded (update: here). It can be generated from the MAVLink package, or you can just grab the the MAVLINK_MESSAGE_CRCS from the sample code.

Tridge explains the seed value in a forum comment.
(tl;dr: it catches when people are using incompatible MAVLink XML files.)
Hi Everyone,
I was the one who added the 'seed' in MAVLink 1.0, so maybe I should explain how it works and why it was added.
 
While we were using MAVLink 0.9 we had a couple of incidents where the XML describing a message that was in active use changed. The change was such that the length of the message didn't change, but the fields did. This meant that when a MAV using the old code was talking to a ground station using updated code that the fields were badly corrupted. The MAVLink 0.9 protocol completely relied on everyone being careful not to change the meaning or format of any existing message. With so many people working on MAVLink this was hard to enforce. 
So for MAVLink 1.0 we decided to solve this problem by adding a 1 byte 'seed' to the checksum based on the XML for the message. When the mavlink code generator runs, it takes a checksum of the XML structure for each message and creates an array define MAVLINK_MESSAGE_CRCS. This is used to initialise the mavlink_message_crcs[] array in the C/C++ implementation, and is similarly used in the python implementation.
When the checksum for a message is calculated, this extra byte is added on the end of the data that that the checksum is calculated over. The result is that if the XML changes then the message will be rejected by the recipient as having an incorrect checksum. This ensures that only messages where the sender and recipient are using the same XML structure will get through (or at least it makes a mistake much more unlikely).
 
If you are doing your own implementation of MAVLink 1.0 you can get this checksum in one of two ways: 
you can just use the generated headers, and use MAVLINK_MESSAGE_CRCS to get the right seed for each message type 
or you can re-implement the code that calculates the seed.
This is the python code that calculates the seed:
 
def message_checksum(msg):
    '''calculate a 8-bit checksum of the key fields of a message, so we
       can detect incompatible XML changes'''
    crc = mavutil.x25crc(msg.name + ' ')
    for f in msg.ordered_fields:
        crc.accumulate(f.type + ' ')
        crc.accumulate(f.name + ' ')
        if f.array_length:
            crc.accumulate(chr(f.array_length))
    return (crc.crc&0xFF) ^ (crc.crc>>8)
 
Notice that it uses the same x25 chcksum that is used at runtime. It calculates a CRC over the message name (such as "RAW_IMU") followed by the type and name of each field, space separated. The order of the fields is the order they are sent over the wire. For arrays, the array length is also added. 
My apologies if this seed has caused any confusion. Hopefully the above will explain the reason it was added.
Cheers, Tridge


(toc: MAVLink)

1 comment:

  1. can you kindly explain what is meant by adding API (for xbee) header and footer in mavlink proxy ? and for that do we have to manually edit in mavlink_helpers.h and save it ? how would my GCS (qgroundcontrol) know about the said changes ??

    thanks !!

    ReplyDelete