protocol.py
Go to the documentation of this file.
1 import datetime
2 import struct
3 from io import BytesIO
4 
5 from . import crc
6 from . utils import *
7 
8 # low-level Protocol (https://tellopilots.com/wiki/protocol/#MessageIDs)
9 START_OF_PACKET = 0xcc
10 SSID_MSG = 0x0011
11 SSID_CMD = 0x0012
12 SSID_PASSWORD_MSG = 0x0013
13 SSID_PASSWORD_CMD = 0x0014
14 WIFI_REGION_MSG = 0x0015
15 WIFI_REGION_CMD = 0x0016
16 WIFI_MSG = 0x001a
17 VIDEO_ENCODER_RATE_CMD = 0x0020
18 VIDEO_DYN_ADJ_RATE_CMD = 0x0021
19 EIS_CMD = 0x0024
20 VIDEO_START_CMD = 0x0025
21 VIDEO_RATE_QUERY = 0x0028
22 TAKE_PICTURE_COMMAND = 0x0030
23 VIDEO_MODE_CMD = 0x0031
24 VIDEO_RECORD_CMD = 0x0032
25 EXPOSURE_CMD = 0x0034
26 LIGHT_MSG = 0x0035
27 JPEG_QUALITY_MSG = 0x0037
28 ERROR_1_MSG = 0x0043
29 ERROR_2_MSG = 0x0044
30 VERSION_MSG = 0x0045
31 TIME_CMD = 0x0046
32 ACTIVATION_TIME_MSG = 0x0047
33 LOADER_VERSION_MSG = 0x0049
34 STICK_CMD = 0x0050
35 TAKEOFF_CMD = 0x0054
36 LAND_CMD = 0x0055
37 FLIGHT_MSG = 0x0056
38 SET_ALT_LIMIT_CMD = 0x0058
39 FLIP_CMD = 0x005c
40 THROW_AND_GO_CMD = 0x005d
41 PALM_LAND_CMD = 0x005e
42 TELLO_CMD_FILE_SIZE = 0x0062 # pt50
43 TELLO_CMD_FILE_DATA = 0x0063 # pt50
44 TELLO_CMD_FILE_COMPLETE = 0x0064 # pt48
45 SMART_VIDEO_CMD = 0x0080
46 SMART_VIDEO_STATUS_MSG = 0x0081
47 LOG_HEADER_MSG = 0x1050
48 LOG_DATA_MSG = 0x1051
49 LOG_CONFIG_MSG = 0x1052
50 BOUNCE_CMD = 0x1053
51 CALIBRATE_CMD = 0x1054
52 LOW_BAT_THRESHOLD_CMD = 0x1055
53 ALT_LIMIT_MSG = 0x1056
54 LOW_BAT_THRESHOLD_MSG = 0x1057
55 ATT_LIMIT_CMD = 0x1058 # Stated incorrectly by Wiki (checked from raw packets)
56 ATT_LIMIT_MSG = 0x1059
57 
58 EMERGENCY_CMD = 'emergency'
59 
60 #Flip commands taken from Go version of code
61 #FlipFront flips forward.
62 FlipFront = 0
63 #FlipLeft flips left.
64 FlipLeft = 1
65 #FlipBack flips backwards.
66 FlipBack = 2
67 #FlipRight flips to the right.
68 FlipRight = 3
69 #FlipForwardLeft flips forwards and to the left.
70 FlipForwardLeft = 4
71 #FlipBackLeft flips backwards and to the left.
72 FlipBackLeft = 5
73 #FlipBackRight flips backwards and to the right.
74 FlipBackRight = 6
75 #FlipForwardRight flips forwards and to the right.
76 FlipForwardRight = 7
77 
78 class Packet(object):
79  def __init__(self, cmd, pkt_type=0x68, payload=b''):
80  if isinstance(cmd, str):
81  self.buf = bytearray()
82  for c in cmd:
83  self.buf.append(ord(c))
84  elif isinstance(cmd, (bytearray, bytes)):
85  self.buf = bytearray()
86  self.buf[:] = cmd
87  else:
88  self.buf = bytearray([
89  START_OF_PACKET,
90  0, 0,
91  0,
92  pkt_type,
93  (cmd & 0xff), ((cmd >> 8) & 0xff),
94  0, 0])
95  self.buf.extend(payload)
96 
97  def fixup(self, seq_num=0):
98  buf = self.get_buffer()
99  if buf[0] == START_OF_PACKET:
100  buf[1], buf[2] = le16(len(buf)+2)
101  buf[1] = (buf[1] << 3)
102  buf[3] = crc.crc8(buf[0:3])
103  buf[7], buf[8] = le16(seq_num)
104  self.add_int16(crc.crc16(buf))
105 
106  def get_buffer(self):
107  return self.buf
108 
109  def get_data(self):
110  return self.buf[9:len(self.buf)-2]
111 
112  def add_byte(self, val):
113  self.buf.append(val & 0xff)
114 
115  def add_int16(self, val):
116  self.add_byte(val)
117  self.add_byte(val >> 8)
118 
119  def add_time(self, time=datetime.datetime.now()):
120  self.add_int16(time.hour)
121  self.add_int16(time.minute)
122  self.add_int16(time.second)
123  self.add_int16(int(time.microsecond/1000) & 0xff)
124  self.add_int16((int(time.microsecond/1000) >> 8) & 0xff)
125 
126  def get_time(self, buf=None):
127  if buf is None:
128  buf = self.get_data()[1:]
129  hour = int16(buf[0], buf[1])
130  min = int16(buf[2], buf[3])
131  sec = int16(buf[4], buf[5])
132  millisec = int16(buf[6], buf[8])
133  now = datetime.datetime.now()
134  return datetime.datetime(now.year, now.month, now.day, hour, min, sec, millisec)
135 
136 
137 class FlightData(object):
138  def __init__(self, data):
139  self.battery_low = 0
140  self.battery_lower = 0
142  self.battery_state = 0
143  self.camera_state = 0
147  self.drone_hover = 0
148  self.em_open = 0
149  self.em_sky = 0
150  self.em_ground = 0
151  self.east_speed = 0
153  self.factory_mode = 0
154  self.fly_mode = 0
155  self.fly_speed = 0
156  self.fly_time = 0
157  self.front_in = 0
158  self.front_lsc = 0
159  self.front_out = 0
160  self.gravity_state = 0
161  self.ground_speed = 0
162  self.height = 0
164  self.imu_state = 0
165  self.light_strength = 0
166  self.north_speed = 0
168  self.power_state = 0
169  self.pressure_state = 0
173  self.wifi_disturb = 0
174  self.wifi_strength = 0
175  self.wind_state = 0
176 
177  if len(data) < 24:
178  return
179 
180  self.height = int16(data[0], data[1])
181  self.north_speed = int16(data[2], data[3])
182  self.east_speed = int16(data[4], data[5])
183  self.ground_speed = int16(data[6], data[7])
184  self.fly_time = int16(data[8], data[9])
185 
186  self.imu_state = ((data[10] >> 0) & 0x1)
187  self.pressure_state = ((data[10] >> 1) & 0x1)
188  self.down_visual_state = ((data[10] >> 2) & 0x1)
189  self.power_state = ((data[10] >> 3) & 0x1)
190  self.battery_state = ((data[10] >> 4) & 0x1)
191  self.gravity_state = ((data[10] >> 5) & 0x1)
192  self.wind_state = ((data[10] >> 7) & 0x1)
193 
194  self.imu_calibration_state = data[11]
195  self.battery_percentage = data[12]
196  self.drone_battery_left = int16(data[13], data[14])
197  self.drone_fly_time_left = int16(data[15], data[16])
198 
199  self.em_sky = ((data[17] >> 0) & 0x1)
200  self.em_ground = ((data[17] >> 1) & 0x1)
201  self.em_open = ((data[17] >> 2) & 0x1)
202  self.drone_hover = ((data[17] >> 3) & 0x1)
203  self.outage_recording = ((data[17] >> 4) & 0x1)
204  self.battery_low = ((data[17] >> 5) & 0x1)
205  self.battery_lower = ((data[17] >> 6) & 0x1)
206  self.factory_mode = ((data[17] >> 7) & 0x1)
207 
208  self.fly_mode = data[18]
209  self.throw_fly_timer = data[19]
210  self.camera_state = data[20]
211  self.electrical_machinery_state = data[21]
212 
213  self.front_in = ((data[22] >> 0) & 0x1)
214  self.front_out = ((data[22] >> 1) & 0x1)
215  self.front_lsc = ((data[22] >> 2) & 0x1)
216 
217  self.temperature_height = ((data[23] >> 0) & 0x1)
218 
219  def __str__(self):
220  return (
221  ("ALT: %2d" % self.height) +
222  (" | SPD: %2d" % self.ground_speed) +
223  (" | BAT: %2d" % self.battery_percentage) +
224  (" | WIFI: %2d" % self.wifi_strength) +
225  (" | CAM: %2d" % self.camera_state) +
226  (" | MODE: %2d" % self.fly_mode) +
227  # (", drone_battery_left=0x%04x" % self.drone_battery_left) +
228  "")
229 
230 class DownloadedFile(object):
231  def __init__(self, filenum, size):
232  self.filenum = filenum
233  self.size = size
234  self.bytes_recieved = 0
235  self.chunks_received = [0x00] * int((size / 1024 + 1) / 8 + 1)
236  self.buffer = BytesIO()
237 
238  def done(self):
239  return self.bytes_recieved >= self.size
240 
241  def data(self):
242  return self.buffer.getvalue()
243 
244  def haveFragment(self, chunk, fragment):
245  return self.chunks_received[chunk] & (1<<(fragment%8))
246 
247  def recvFragment(self, chunk, fragment, size, data):
248  if self.haveFragment(chunk, fragment):
249  return False
250  # Mark a fragment as received.
251  # Returns true if we have all fragments making up that chunk now.
252  self.buffer.seek(fragment*1024)
253  self.buffer.write(data)
254  self.bytes_recieved += size
255  self.chunks_received[chunk] |= (1<<(fragment%8))
256  return self.chunks_received[chunk] == 0xFF
257 
258 
259 class VideoData(object):
260  packets_per_frame = 0
261  def __init__(self, data):
262  self.h0 = byte(data[0])
263  self.h1 = byte(data[1])
264  if VideoData.packets_per_frame < (self.h1 & 0x7f):
265  VideoData.packets_per_frame = (self.h1 & 0x7f)
266 
267  def gap(self, video_data):
268  if video_data is None:
269  return 0
270 
271  v0 = self
272  v1 = video_data
273 
274  loss = 0
275  if ((v0.h0 != v1.h0 and v0.h0 != ((v1.h0 + 1) & 0xff))
276  or (v0.h0 != v1.h0 and (v0.h1 & 0x7f) != 00)
277  or (v0.h0 == v1.h0 and (v0.h1 & 0x7f) != (v1.h1 & 0x7f) + 1)):
278  loss = v0.h0 - v1.h0
279  if loss < 0:
280  loss = loss + 256
281  loss = loss * VideoData.packets_per_frame + ((v0.h1 & 0x7f) - (v1.h1 & 0x7f) - 1)
282 
283  return loss
284 
285 
286 class LogData(object):
287  ID_NEW_MVO_FEEDBACK = 29
288  ID_IMU_ATTI = 2048
289  unknowns = []
290 
291  def __init__(self, log, data = None):
292  self.log = log
293  self.count = 0
294  self.mvo = LogNewMvoFeedback(log)
295  self.imu = LogImuAtti(log)
296  if data:
297  self.update(data)
298 
299  def __str__(self):
300  return ('MVO: ' + str(self.mvo) +
301  '|IMU: ' + str(self.imu) +
302  "")
303 
304  def format_cvs(self):
305  return (
306  self.mvo.format_cvs() +
307  ',' + self.imu.format_cvs() +
308  "")
309 
310  def format_cvs_header(self):
311  return (
312  self.mvo.format_cvs_header() +
313  ',' + self.imu.format_cvs_header() +
314  "")
315 
316  def update(self, data):
317  if isinstance(data, bytearray):
318  data = str(data)
319 
320  self.log.debug('LogData: data length=%d' % len(data))
321  self.count += 1
322  pos = 0
323  while (pos < len(data) - 2):
324  if (struct.unpack_from('B', data, pos+0)[0] != 0x55):
325  raise Exception('LogData: corrupted data at pos=%d, data=%s'
326  % (pos, byte_to_hexstring(data[pos:])))
327  length = struct.unpack_from('<h', data, pos+1)[0]
328  checksum = data[pos+3]
329  id = struct.unpack_from('<H', data, pos+4)[0]
330  # 4bytes data[6:9] is tick
331  # last 2 bytes are CRC
332  # length-12 is the byte length of payload
333  xorval = data[pos+6]
334  if isinstance(data, str):
335  payload = bytearray([ord(x) ^ ord(xorval) for x in data[pos+10:pos+10+length-12]])
336  else:
337  payload = bytearray([x ^ xorval for x in data[pos+10:pos+10+length-12]])
338  if id == self.ID_NEW_MVO_FEEDBACK:
339  self.mvo.update(payload, self.count)
340  elif id == self.ID_IMU_ATTI:
341  self.imu.update(payload, self.count)
342  else:
343  if not id in self.unknowns:
344  self.log.info('LogData: UNHANDLED LOG DATA: id=%5d, length=%4d' % (id, length-12))
345  self.unknowns.append(id)
346 
347  pos += length
348  if pos != len(data) - 2:
349  raise Exception('LogData: corrupted data at pos=%d, data=%s'
350  % (pos, byte_to_hexstring(data[pos:])))
351 
352 
353 class LogNewMvoFeedback(object):
354  def __init__(self, log = None, data = None):
355  self.log = log
356  self.count = 0
357  self.vel_x = 0.0
358  self.vel_y = 0.0
359  self.vel_z = 0.0
360  self.pos_x = 0.0
361  self.pos_y = 0.0
362  self.pos_z = 0.0
363  if (data != None):
364  self.update(data, count)
365 
366  def __str__(self):
367  return (
368  ("VEL: %5.2f %5.2f %5.2f" % (self.vel_x, self.vel_y, self.vel_z))+
369  (" POS: %5.2f %5.2f %5.2f" % (self.pos_x, self.pos_y, self.pos_z))+
370  "")
371 
372  def format_cvs(self):
373  return (
374  ("%f,%f,%f" % (self.vel_x, self.vel_y, self.vel_z))+
375  (",%f,%f,%f" % (self.pos_x, self.pos_y, self.pos_z))+
376  "")
377 
378  def format_cvs_header(self):
379  return (
380  "mvo.vel_x,mvo.vel_y,mvo.vel_z" +
381  ",mvo.pos_x,mvo.pos_y,mvo.pos_z" +
382  "")
383 
384  def update(self, data, count = 0):
385  self.log.debug('LogNewMvoFeedback: length=%d %s' % (len(data), byte_to_hexstring(data)))
386  self.count = count
387  (self.vel_x, self.vel_y, self.vel_z) = struct.unpack_from('<hhh', data, 2)
388  self.vel_x /= 100.0
389  self.vel_y /= 100.0
390  self.vel_z /= 100.0
391  (self.pos_x, self.pos_y, self.pos_z) = struct.unpack_from('fff', data, 8)
392  self.log.debug('LogNewMvoFeedback: ' + str(self))
393 
394 
395 class LogImuAtti(object):
396  def __init__(self, log = None, data = None):
397  self.log = log
398  self.count = 0
399  self.acc_x = 0.0
400  self.acc_y = 0.0
401  self.acc_z = 0.0
402  self.gyro_x = 0.0
403  self.gyro_y = 0.0
404  self.gyro_z = 0.0
405  self.q0 = 0.0
406  self.q1 = 0.0
407  self.q2 = 0.0
408  self.q3 = 0.0
409  self.vg_x = 0.0
410  self.vg_y = 0.0
411  self.vg_z = 0.0
412  if (data != None):
413  self.update(data)
414 
415  def __str__(self):
416  return (
417  ("ACC: %5.2f %5.2f %5.2f" % (self.acc_x, self.acc_y, self.acc_z)) +
418  (" GYRO: %5.2f %5.2f %5.2f" % (self.gyro_x, self.gyro_y, self.gyro_z)) +
419  (" QUATERNION: %5.2f %5.2f %5.2f %5.2f" % (self.q0, self.q1, self.q2, self.q3)) +
420  (" VG: %5.2f %5.2f %5.2f" % (self.vg_x, self.vg_y, self.vg_z)) +
421  "")
422 
423  def format_cvs(self):
424  return (
425  ("%f,%f,%f" % (self.acc_x, self.acc_y, self.acc_z)) +
426  (",%f,%f,%f" % (self.gyro_x, self.gyro_y, self.gyro_z)) +
427  (",%f,%f,%f,%f" % (self.q0, self.q1, self.q2, self.q3)) +
428  (",%f,%f,%f" % (self.vg_x, self.vg_y, self.vg_z)) +
429  "")
430 
431  def format_cvs_header(self):
432  return (
433  "imu.acc_x,imu.acc_y,imu.acc_z" +
434  ",imu.gyro_x,imu.gyro_y,imu.gyro_z" +
435  ",imu.q0,imu.q1,imu.q2, self.q3" +
436  ",imu.vg_x,imu.vg_y,imu.vg_z" +
437  "")
438 
439  def update(self, data, count = 0):
440  self.log.debug('LogImuAtti: length=%d %s' % (len(data), byte_to_hexstring(data)))
441  self.count = count
442  (self.acc_x, self.acc_y, self.acc_z) = struct.unpack_from('fff', data, 20)
443  (self.gyro_x, self.gyro_y, self.gyro_z) = struct.unpack_from('fff', data, 32)
444  (self.q0, self.q1, self.q2, self.q3) = struct.unpack_from('ffff', data, 48)
445  (self.vg_x, self.vg_y, self.vg_z) = struct.unpack_from('fff', data, 76)
446  self.log.debug('LogImuAtti: ' + str(self))
def __init__(self, log, data=None)
Definition: protocol.py:291
def int16(val0, val1)
Definition: utils.py:19
def __init__(self, log=None, data=None)
Definition: protocol.py:354
def add_time(self, time=datetime.datetime.now())
Definition: protocol.py:119
def haveFragment(self, chunk, fragment)
Definition: protocol.py:244
def gap(self, video_data)
Definition: protocol.py:267
def byte_to_hexstring(buf)
Definition: utils.py:26
def __init__(self, log=None, data=None)
Definition: protocol.py:396
def recvFragment(self, chunk, fragment, size, data)
Definition: protocol.py:247
def fixup(self, seq_num=0)
Definition: protocol.py:97
def update(self, data, count=0)
Definition: protocol.py:439
def __init__(self, cmd, pkt_type=0x68, payload=b'')
Definition: protocol.py:79
def get_time(self, buf=None)
Definition: protocol.py:126
def __init__(self, filenum, size)
Definition: protocol.py:231


tello_driver
Author(s): Jordy van Appeven
autogenerated on Wed May 13 2020 03:34:54