gobot.io/x/gobot/v2@v2.1.0/platforms/mavlink/common/mavlink.go (about)

     1  package mavlink
     2  
     3  //
     4  // MAVLink comm protocol built from common.xml
     5  // http://pixhawk.ethz.ch/software/mavlink
     6  //
     7  
     8  import (
     9  	"bytes"
    10  	"encoding/binary"
    11  	"io"
    12  	"time"
    13  )
    14  
    15  const (
    16  	MAVLINK_BIG_ENDIAN     = 0
    17  	MAVLINK_LITTLE_ENDIAN  = 1
    18  	MAVLINK_10_STX         = 254
    19  	MAVLINK_20_STX         = 253
    20  	MAVLINK_ENDIAN         = MAVLINK_LITTLE_ENDIAN
    21  	MAVLINK_ALIGNED_FIELDS = 1
    22  	MAVLINK_CRC_EXTRA      = 1
    23  	X25_INIT_CRC           = 0xffff
    24  	X25_VALIDATE_CRC       = 0xf0b8
    25  )
    26  
    27  var sequence uint16 = 0
    28  
    29  func generateSequence() uint8 {
    30  	sequence = (sequence + 1) % 256
    31  	return uint8(sequence)
    32  }
    33  
    34  // The MAVLinkMessage interface is implemented by MAVLink messages
    35  type MAVLinkMessage interface {
    36  	Id() uint8
    37  	Len() uint8
    38  	Crc() uint8
    39  	Pack() []byte
    40  	Decode([]byte)
    41  }
    42  
    43  // A MAVLinkPacket represents a raw packet received from a micro air vehicle
    44  type MAVLinkPacket struct {
    45  	Protocol    uint8
    46  	Length      uint8
    47  	Sequence    uint8
    48  	SystemID    uint8
    49  	ComponentID uint8
    50  	MessageID   uint8
    51  	Data        []uint8
    52  	Checksum    uint16
    53  }
    54  
    55  // ReadMAVLinkPacket reads an io.Reader for a new packet and returns a new MAVLink packet
    56  // or returns the error received by the io.Reader
    57  func ReadMAVLinkPacket(r io.Reader) (*MAVLinkPacket, error) {
    58  	for {
    59  		header, err := read(r, 1)
    60  		if err != nil {
    61  			return nil, err
    62  		}
    63  		if header[0] == 254 {
    64  			length, err := read(r, 1)
    65  			if err != nil {
    66  				return nil, err
    67  			} else if length[0] > 250 {
    68  				continue
    69  			}
    70  			m := &MAVLinkPacket{}
    71  			data, err := read(r, int(length[0])+7)
    72  			if err != nil {
    73  				return nil, err
    74  			}
    75  			data = append([]byte{header[0], length[0]}, data...)
    76  			m.Decode(data)
    77  			return m, nil
    78  		}
    79  	}
    80  }
    81  
    82  // CraftMAVLinkPacket returns a new MAVLinkPacket from a MAVLinkMessage
    83  func CraftMAVLinkPacket(SystemID uint8, ComponentID uint8, Message MAVLinkMessage) *MAVLinkPacket {
    84  	return NewMAVLinkPacket(
    85  		0xFE,
    86  		Message.Len(),
    87  		generateSequence(),
    88  		SystemID,
    89  		ComponentID,
    90  		Message.Id(),
    91  		Message.Pack(),
    92  	)
    93  }
    94  
    95  // NewMAVLinkPacket returns a new MAVLinkPacket
    96  func NewMAVLinkPacket(Protocol uint8, Length uint8, Sequence uint8, SystemID uint8, ComponentID uint8, MessageID uint8, Data []uint8) *MAVLinkPacket {
    97  	m := &MAVLinkPacket{
    98  		Protocol:    Protocol,
    99  		Length:      Length,
   100  		Sequence:    Sequence,
   101  		SystemID:    SystemID,
   102  		ComponentID: ComponentID,
   103  		MessageID:   MessageID,
   104  		Data:        Data,
   105  	}
   106  	m.Checksum = crcCalculate(m)
   107  	return m
   108  }
   109  
   110  // MAVLinkMessage returns the decoded MAVLinkMessage from the MAVLinkPacket
   111  // or returns an error generated from the MAVLinkMessage
   112  func (m *MAVLinkPacket) MAVLinkMessage() (MAVLinkMessage, error) {
   113  	return NewMAVLinkMessage(m.MessageID, m.Data)
   114  }
   115  
   116  // Pack returns a packed byte array which represents the MAVLinkPacket
   117  func (m *MAVLinkPacket) Pack() []byte {
   118  	data := new(bytes.Buffer)
   119  	binary.Write(data, binary.LittleEndian, m.Protocol)
   120  	binary.Write(data, binary.LittleEndian, m.Length)
   121  	binary.Write(data, binary.LittleEndian, m.Sequence)
   122  	binary.Write(data, binary.LittleEndian, m.SystemID)
   123  	binary.Write(data, binary.LittleEndian, m.ComponentID)
   124  	binary.Write(data, binary.LittleEndian, m.MessageID)
   125  	data.Write(m.Data)
   126  	binary.Write(data, binary.LittleEndian, m.Checksum)
   127  	return data.Bytes()
   128  }
   129  
   130  // Decode accepts a packed byte array and populates the fields of the MAVLinkPacket
   131  func (m *MAVLinkPacket) Decode(buf []byte) {
   132  	m.Protocol = buf[0]
   133  	m.Length = buf[1]
   134  	m.Sequence = buf[2]
   135  	m.SystemID = buf[3]
   136  	m.ComponentID = buf[4]
   137  	m.MessageID = buf[5]
   138  	m.Data = buf[6 : 6+int(m.Length)]
   139  	checksum := buf[7+int(m.Length):]
   140  	m.Checksum = uint16(checksum[1])<<8 | uint16(checksum[0])
   141  }
   142  
   143  func read(r io.Reader, length int) ([]byte, error) {
   144  	buf := []byte{}
   145  	for length > 0 {
   146  		tmp := make([]byte, length)
   147  		i, err := r.Read(tmp[:])
   148  		if err != nil {
   149  			return nil, err
   150  		} else {
   151  			length -= i
   152  			buf = append(buf, tmp...)
   153  			if length != i {
   154  				time.Sleep(1 * time.Millisecond)
   155  			} else {
   156  				break
   157  			}
   158  		}
   159  	}
   160  	return buf, nil
   161  }
   162  
   163  //
   164  // Accumulate the X.25 CRC by adding one char at a time.
   165  //
   166  // The checksum function adds the hash of one char at a time to the
   167  // 16 bit checksum (uint16).
   168  //
   169  // data to hash
   170  // crcAccum the already accumulated checksum
   171  //
   172  func crcAccumulate(data uint8, crcAccum uint16) uint16 {
   173  	/*Accumulate one byte of data into the CRC*/
   174  	var tmp uint8
   175  
   176  	tmp = data ^ (uint8)(crcAccum&0xff)
   177  	tmp ^= (tmp << 4)
   178  	crcAccum = (uint16(crcAccum) >> 8) ^ (uint16(tmp) << 8) ^ (uint16(tmp) << 3) ^ (uint16(tmp) >> 4)
   179  	return crcAccum
   180  }
   181  
   182  //
   183  // Initiliaze the buffer for the X.25 CRC
   184  //
   185  func crcInit() uint16 {
   186  	return X25_INIT_CRC
   187  }
   188  
   189  //
   190  // Calculates the X.25 checksum on a byte buffer
   191  //
   192  // return the checksum over the buffer bytes
   193  //
   194  func crcCalculate(m *MAVLinkPacket) uint16 {
   195  	crc := crcInit()
   196  
   197  	for _, v := range m.Pack()[1 : m.Length+6] {
   198  		crc = crcAccumulate(v, crc)
   199  	}
   200  	message, _ := m.MAVLinkMessage()
   201  	crc = crcAccumulate(message.Crc(), crc)
   202  	return crc
   203  }