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 }