mrtautopilot 8__tar.gz → 10__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mrtautopilot
3
- Version: 8
3
+ Version: 10
4
4
  Summary: Communicate with the MRT Autopilot Mavlink API
5
5
  Maintainer-email: Nathan Knotts <nathan@magothyrt.com>
6
6
  Project-URL: Homepage, https://www.magothyrt.com/
@@ -1,4 +1,4 @@
1
- __version__ = "8"
1
+ __version__ = "10"
2
2
 
3
3
  from .autopilot import *
4
4
  from .mission import *
@@ -3,12 +3,13 @@ import threading
3
3
  import multiprocessing
4
4
  import socket
5
5
  import asyncio
6
+ import datetime
6
7
  import logging
7
8
  import struct
8
9
  import uuid
9
10
  from dataclasses import dataclass
10
11
  from enum import Enum
11
- from typing import List, Union, Tuple
12
+ from typing import List, Union, Tuple, Callable
12
13
 
13
14
  import mrtmavlink
14
15
  from . import mission
@@ -267,6 +268,187 @@ class LowBandwidth:
267
268
  fault_response: Union[HealthResponse, None]
268
269
 
269
270
 
271
+ class GpsFixType(Enum):
272
+ NoGps = 0
273
+ NoFix = 1
274
+ Fix2d = 2
275
+ Fix3d = 3
276
+ Dgps = 4
277
+ RtkFloat = 5
278
+ RtkFixed = 6
279
+ Static = 7
280
+ Ppp = 8
281
+
282
+
283
+ @dataclass
284
+ class Gps2Raw:
285
+ def __init__(self, msg: mrtmavlink.MAVLink_gps2_raw_message):
286
+ self.timestamp = datetime.datetime.fromtimestamp(
287
+ msg.time_usec / 1e6, tz=datetime.timezone.utc
288
+ )
289
+ self.fix_type = GpsFixType(msg.fix_type)
290
+
291
+ if msg.lat != 0x7FFFFFFF:
292
+ self.latitude_deg = msg.lat / 1e7
293
+ else:
294
+ self.latitude_deg = None
295
+
296
+ if msg.lon != 0x7FFFFFFF:
297
+ self.longitude_deg = msg.lon / 1e7
298
+ else:
299
+ self.longitude_deg = None
300
+
301
+ if msg.alt != 0x7FFFFFF:
302
+ self.altitude_m = msg.alt / 1000
303
+ else:
304
+ self.altitude_m = None
305
+
306
+ if msg.eph != 0xFFFF:
307
+ self.hdop = msg.eph / 100
308
+ else:
309
+ self.hdop = None
310
+
311
+ if msg.epv != 0xFFFF:
312
+ self.vdop = msg.epv / 100
313
+ else:
314
+ self.vdop = None
315
+
316
+ if msg.vel != 0xFFFF:
317
+ self.velocity_mps = msg.vel / 100
318
+ else:
319
+ self.velocity_mps = None
320
+
321
+ if msg.cog != 0xFFFF:
322
+ self.course_deg = msg.cog / 100
323
+ else:
324
+ self.course_deg = None
325
+
326
+ if msg.satellites_visible != 0xFF:
327
+ self.num_set = msg.satellites_visible
328
+ else:
329
+ self.num_set = None
330
+
331
+ timestamp: datetime.datetime
332
+ fix_type: GpsFixType
333
+ latitude_deg: Union[float, None]
334
+ longitude_deg: Union[float, None]
335
+ altitude_m: Union[float, None]
336
+ hdop: Union[float, None]
337
+ vdop: Union[float, None]
338
+ velocity_mps: Union[float, None]
339
+ course_deg: Union[float, None]
340
+ num_set: Union[int, None]
341
+
342
+
343
+ @dataclass
344
+ class ParamUint8:
345
+ value: int
346
+
347
+
348
+ @dataclass
349
+ class ParamInt8:
350
+ value: int
351
+
352
+
353
+ @dataclass
354
+ class ParamUint16:
355
+ value: int
356
+
357
+
358
+ @dataclass
359
+ class ParamInt16:
360
+ value: int
361
+
362
+
363
+ @dataclass
364
+ class ParamUint32:
365
+ value: int
366
+
367
+
368
+ @dataclass
369
+ class ParamInt32:
370
+ value: int
371
+
372
+
373
+ @dataclass
374
+ class ParamReal32:
375
+ value: float
376
+
377
+
378
+ ParamValueType = Union[
379
+ ParamUint8,
380
+ ParamInt8,
381
+ ParamUint16,
382
+ ParamInt16,
383
+ ParamUint32,
384
+ ParamInt32,
385
+ ParamReal32,
386
+ ]
387
+
388
+
389
+ @dataclass
390
+ class ParamValue:
391
+ @staticmethod
392
+ def from_mavlink(msg: mrtmavlink.MAVLink_param_value_message):
393
+ buffer = struct.pack("<f", msg.param_value)
394
+ if msg.param_type == mrtmavlink.MAV_PARAM_TYPE_UINT8:
395
+ value = ParamUint8(int(struct.unpack("BBBB", buffer)[0]))
396
+ elif msg.param_type == mrtmavlink.MAV_PARAM_TYPE_INT8:
397
+ value = ParamInt8(int(struct.unpack("bBBB", buffer)[0]))
398
+ elif msg.param_type == mrtmavlink.MAV_PARAM_TYPE_UINT16:
399
+ value = ParamUint16(int(struct.unpack("<HBB", buffer)[0]))
400
+ elif msg.param_type == mrtmavlink.MAV_PARAM_TYPE_INT16:
401
+ value = ParamInt16(int(struct.unpack("<hBB", buffer)[0]))
402
+ elif msg.param_type == mrtmavlink.MAV_PARAM_TYPE_UINT32:
403
+ value = ParamUint32(int(struct.unpack("<I", buffer)[0]))
404
+ elif msg.param_type == mrtmavlink.MAV_PARAM_TYPE_INT32:
405
+ value = ParamInt32(int(struct.unpack("<i", buffer)[0]))
406
+ elif msg.param_type == mrtmavlink.MAV_PARAM_TYPE_REAL32:
407
+ value = ParamReal32(float(struct.unpack("<f", buffer)[0]))
408
+ else:
409
+ raise ValueError(f"Unsupported parameter type: {msg.param_type}")
410
+
411
+ return ParamValue(msg.param_id, value, msg.param_index, msg.param_count)
412
+
413
+ def to_mavlink(self) -> mrtmavlink.MAVLink_param_value_message:
414
+ if isinstance(self.value, ParamUint8):
415
+ typ = mrtmavlink.MAV_PARAM_TYPE_UINT8
416
+ value = struct.pack("BBBB", self.value.value, 0, 0, 0)
417
+ elif isinstance(self.value, ParamInt8):
418
+ typ = mrtmavlink.MAV_PARAM_TYPE_INT8
419
+ value = struct.pack("bBBB", self.value.value, 0, 0, 0)
420
+ elif isinstance(self.value, ParamUint16):
421
+ typ = mrtmavlink.MAV_PARAM_TYPE_UINT16
422
+ value = struct.pack("<HBB", self.value.value, 0, 0)
423
+ elif isinstance(self.value, ParamInt16):
424
+ typ = mrtmavlink.MAV_PARAM_TYPE_INT16
425
+ value = struct.pack("<hBB", self.value.value, 0, 0)
426
+ elif isinstance(self.value, ParamUint32):
427
+ typ = mrtmavlink.MAV_PARAM_TYPE_UINT32
428
+ value = struct.pack("<I", self.value.value)
429
+ elif isinstance(self.value, ParamInt32):
430
+ typ = mrtmavlink.MAV_PARAM_TYPE_INT32
431
+ value = struct.pack("<i", self.value.value)
432
+ elif isinstance(self.value, ParamReal32):
433
+ typ = mrtmavlink.MAV_PARAM_TYPE_REAL32
434
+ value = struct.pack("<f", self.value.value)
435
+ else:
436
+ raise ValueError(f"Unsupported parameter type: {type(self.value)}")
437
+
438
+ return mrtmavlink.MAVLink_param_value_message(
439
+ param_id=self.name.encode("utf-8"),
440
+ param_value=struct.unpack("f", value)[0],
441
+ param_type=typ,
442
+ param_index=self.param_idx,
443
+ param_count=self.num_param,
444
+ )
445
+
446
+ name: str
447
+ value: ParamValueType
448
+ param_idx: int
449
+ num_param: int
450
+
451
+
270
452
  class MavlinkThread:
271
453
  def __init__(
272
454
  self,
@@ -278,6 +460,7 @@ class MavlinkThread:
278
460
  ):
279
461
  self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
280
462
  self.sock.setblocking(False)
463
+ self.msg_callback: Callable[[mrtmavlink.MAVLink_message], None] = lambda x: None
281
464
 
282
465
  if multicast_group:
283
466
  self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
@@ -297,9 +480,12 @@ class MavlinkThread:
297
480
  self.remote_port = remote_port
298
481
  self.remote_address_list = [remote_address]
299
482
  if remote_address == "127.0.0.1":
300
- self.remote_address_list.extend(
301
- socket.gethostbyname_ex(socket.gethostname())[2]
302
- )
483
+ try:
484
+ self.remote_address_list.extend(
485
+ socket.gethostbyname_ex(socket.gethostname())[2]
486
+ )
487
+ except socket.gaierror:
488
+ logging.warning("Failed to get local IP address")
303
489
 
304
490
  self.is_started = False
305
491
  self.is_started_cv = threading.Condition()
@@ -324,6 +510,9 @@ class MavlinkThread:
324
510
  self.expected_cmd_uuid = 0
325
511
  self.ack_result: Union[int, None] = None
326
512
 
513
+ self.is_requesting_parameters = False
514
+ self.param_value: Union[ParamValue, None] = None
515
+
327
516
  def write(self, data: bytes):
328
517
  self.sock.sendto(data, (self.remote_address, self.remote_port))
329
518
 
@@ -363,15 +552,25 @@ class MavlinkThread:
363
552
  for msg in messages:
364
553
  self._msg_callback(msg)
365
554
 
555
+ async def _notify_param_result(self, result: ParamValue):
556
+ async with self.param_cond:
557
+ self.param_result = result
558
+ self.param_cond.notify()
559
+
366
560
  async def _notify_ack_result(self, result: int):
367
561
  async with self.ack_cond:
368
562
  self.ack_result = result
369
563
  self.ack_cond.notify()
370
564
 
371
- def _msg_callback(self, msg: mrtmavlink.MAVLink_message):
372
- logging.debug(f"Received: {msg.msgname}, addr")
565
+ def register_message_callback(
566
+ self, callback: Callable[[mrtmavlink.MAVLink_message], None]
567
+ ):
568
+ self.msg_callback = callback
373
569
 
570
+ def _msg_callback(self, msg: mrtmavlink.MAVLink_message):
571
+ self.msg_callback(msg)
374
572
  sys_id = msg.get_srcSystem()
573
+ logging.debug(f"Received: {msg.msgname}, sys_id={sys_id}, {msg}")
375
574
  if self.system_id != sys_id:
376
575
  self.system_id = msg.get_srcSystem()
377
576
  logging.info(f"Setting System ID to {sys_id}")
@@ -411,6 +610,10 @@ class MavlinkThread:
411
610
  ):
412
611
  logging.debug(f"Received Command Ack: {msg}")
413
612
  self.loop.create_task(self._notify_ack_result(msg.result))
613
+ elif type(msg) is mrtmavlink.MAVLink_param_value_message:
614
+ param = ParamValue.from_mavlink(msg)
615
+ logging.debug(f"Received Param Value: {param}")
616
+ self.loop.create_task(self._notify_param_result(param))
414
617
 
415
618
  def send_heartbeat(self):
416
619
  self.loop.call_soon_threadsafe(lambda: self._send_heartbeat())
@@ -421,55 +624,150 @@ class MavlinkThread:
421
624
  mrtmavlink.MAV_TYPE_GCS, mrtmavlink.MAV_AUTOPILOT_INVALID, 0, 0, 0
422
625
  )
423
626
 
627
+ def get_parameter(
628
+ self,
629
+ id: str,
630
+ callback: Callable[[Union[ParamValue, None]], None],
631
+ ):
632
+ self.loop.create_task(self._get_parameter(id, callback))
633
+
634
+ async def _get_parameter(
635
+ self,
636
+ id: str,
637
+ callback: Callable[[Union[ParamValue, None]], None],
638
+ ):
639
+ assert len(id) <= 16
640
+ if self.system_id is None:
641
+ logging.info("Failed to get parameter, system_id not set")
642
+ return
643
+
644
+ if self.is_requesting_parameters:
645
+ logging.warning("Already requesting parameters")
646
+ callback(None)
647
+ return
648
+
649
+ RETRY_PERIOD_S = 0.25
650
+ try:
651
+ self.is_requesting_parameters = True
652
+
653
+ logging.info(f"Getting Param {id}")
654
+
655
+ self.param_result = None
656
+ self.conn.param_request_read_send(
657
+ self.system_id,
658
+ mrtmavlink.MAV_COMP_ID_AUTOPILOT1,
659
+ id.encode("utf-8"),
660
+ -1,
661
+ )
662
+ try:
663
+ async with self.param_cond:
664
+ await asyncio.wait_for(self.param_cond.wait(), RETRY_PERIOD_S)
665
+ except asyncio.TimeoutError:
666
+ logging.error("Timeout waiting for parameter ack")
667
+ callback(None)
668
+ return
669
+
670
+ if self.param_result is not None:
671
+ if self.param_result.name == id:
672
+ callback(self.param_result)
673
+ else:
674
+ logging.warning(
675
+ f"Set parameter {id} but got {self.param_result.name}"
676
+ )
677
+ callback(None)
678
+ else:
679
+ logging.warning(f"Set parameter {id} but got None")
680
+ callback(None)
681
+ finally:
682
+ self.is_requesting_parameters = False
683
+
424
684
  def set_parameter(
425
685
  self,
426
686
  id: str,
427
- value: float,
428
- typ: int,
429
- component: int = mrtmavlink.MAV_COMP_ID_AUTOPILOT1,
687
+ value: ParamValueType,
688
+ callback: Callable[[Union[ParamValue, None]], None] = lambda x: None,
430
689
  ):
431
- self.loop.call_soon_threadsafe(
432
- lambda: self._set_parameter(id, value, typ, component)
433
- )
690
+ self.loop.create_task(self._set_parameter(id, value, callback))
434
691
 
435
- def _set_parameter(self, id: str, value: float, typ: int, component: int):
692
+ async def _set_parameter(
693
+ self,
694
+ id: str,
695
+ value: ParamValueType,
696
+ callback: Callable[[Union[ParamValue, None]], None],
697
+ ):
436
698
  assert len(id) <= 16
437
- if not self.system_id:
699
+ if self.system_id is None:
438
700
  logging.info("Failed to set parameter, system_id not set")
439
701
  return
440
702
 
441
- logging.info(f"Setting Param {id} to {value}")
442
-
443
- if typ == mrtmavlink.MAV_PARAM_TYPE_UINT8:
444
- val_bytes = struct.pack("BBBB", int(value), 0, 0, 0)
445
- elif typ == mrtmavlink.MAV_PARAM_TYPE_INT8:
446
- val_bytes = struct.pack("bBBB", int(value), 0, 0, 0)
447
- elif typ == mrtmavlink.MAV_PARAM_TYPE_UINT16:
448
- val_bytes = struct.pack("<HBB", int(value), 0, 0)
449
- elif typ == mrtmavlink.MAV_PARAM_TYPE_INT16:
450
- val_bytes = struct.pack("<hBB", int(value), 0, 0)
451
- elif typ == mrtmavlink.MAV_PARAM_TYPE_UINT32:
452
- val_bytes = struct.pack("<I", int(value))
453
- elif typ == mrtmavlink.MAV_PARAM_TYPE_INT32:
454
- val_bytes = struct.pack("<i", int(value))
455
- elif typ == mrtmavlink.MAV_PARAM_TYPE_REAL32:
456
- val_bytes = struct.pack("<f", value)
457
- else:
458
- assert False, "unsupported type"
459
-
460
- self.conn.param_set_send(
461
- self.system_id,
462
- component,
463
- id.encode("utf-8"),
464
- struct.unpack("f", val_bytes)[0],
465
- typ,
466
- )
703
+ if self.is_requesting_parameters:
704
+ logging.warning("Already requesting parameters")
705
+ callback(None)
706
+ return
707
+
708
+ RETRY_PERIOD_S = 0.25
709
+ try:
710
+ self.is_requesting_parameters = True
711
+
712
+ logging.info(f"Setting Param {id} to {value}")
713
+
714
+ if isinstance(value, ParamUint8):
715
+ val_bytes = struct.pack("BBBB", int(value.value), 0, 0, 0)
716
+ typ = mrtmavlink.MAV_PARAM_TYPE_UINT8
717
+ elif isinstance(value, ParamInt8):
718
+ val_bytes = struct.pack("bBBB", int(value.value), 0, 0, 0)
719
+ typ = mrtmavlink.MAV_PARAM_TYPE_INT8
720
+ elif isinstance(value, ParamUint16):
721
+ val_bytes = struct.pack("<HBB", int(value.value), 0, 0)
722
+ typ = mrtmavlink.MAV_PARAM_TYPE_UINT16
723
+ elif isinstance(value, ParamInt16):
724
+ val_bytes = struct.pack("<hBB", int(value.value), 0, 0)
725
+ typ = mrtmavlink.MAV_PARAM_TYPE_INT16
726
+ elif isinstance(value, ParamUint32):
727
+ val_bytes = struct.pack("<I", int(value.value))
728
+ typ = mrtmavlink.MAV_PARAM_TYPE_UINT32
729
+ elif isinstance(value, ParamInt32):
730
+ val_bytes = struct.pack("<i", int(value.value))
731
+ typ = mrtmavlink.MAV_PARAM_TYPE_INT32
732
+ elif isinstance(value, ParamReal32):
733
+ val_bytes = struct.pack("<f", value.value)
734
+ typ = mrtmavlink.MAV_PARAM_TYPE_REAL32
735
+ else:
736
+ assert False, "unsupported type"
737
+
738
+ self.param_result = None
739
+ self.conn.param_set_send(
740
+ self.system_id,
741
+ mrtmavlink.MAV_COMP_ID_AUTOPILOT1,
742
+ id.encode("utf-8"),
743
+ struct.unpack("f", val_bytes)[0],
744
+ typ,
745
+ )
746
+ try:
747
+ async with self.param_cond:
748
+ await asyncio.wait_for(self.param_cond.wait(), RETRY_PERIOD_S)
749
+ except asyncio.TimeoutError:
750
+ logging.error("Timeout waiting for parameter ack")
751
+ callback(None)
752
+ return
753
+
754
+ if self.param_result is not None:
755
+ if self.param_result.name == id:
756
+ callback(self.param_result)
757
+ else:
758
+ logging.warning(
759
+ f"Set parameter {id} but got {self.param_result.name}"
760
+ )
761
+ callback(None)
762
+ else:
763
+ logging.warning(f"Set parameter {id} but got None")
764
+ callback(None)
765
+ finally:
766
+ self.is_requesting_parameters = False
467
767
 
468
768
  def set_motor_enablement(self, enable: bool):
469
769
  logging.info(f"Setting Motor Enablement to {enable}")
470
- self.set_parameter(
471
- "MOTOR_ENABLE", 1 if enable else 0, mrtmavlink.MAV_PARAM_TYPE_UINT8
472
- )
770
+ self.set_parameter("MOTOR_ENABLE", ParamUint8(1 if enable else 0))
473
771
 
474
772
  async def _run(self):
475
773
  with self.is_started_cv:
@@ -478,6 +776,7 @@ class MavlinkThread:
478
776
 
479
777
  self.loop = asyncio.get_running_loop()
480
778
  self.ack_cond = asyncio.Condition()
779
+ self.param_cond = asyncio.Condition()
481
780
  await self.loop.create_datagram_endpoint(lambda: self, sock=self.sock) # type: ignore
482
781
 
483
782
  while not self.is_done:
@@ -491,11 +790,57 @@ class MavlinkThread:
491
790
  def _send_system_time(self):
492
791
  self.conn.system_time_send(int(time.time() * 1e6), 0)
493
792
 
793
+ def _send_param_request_list(self):
794
+ self.conn.param_request_list_send(
795
+ self.system_id, # target_system
796
+ mrtmavlink.MAV_COMP_ID_AUTOPILOT1, # target_component
797
+ )
798
+
799
+ def request_parameters(self, callback: Callable[[List[ParamValue]], None]):
800
+ self.loop.create_task(self._request_parameters(callback))
801
+
802
+ async def _request_parameters(self, callback: Callable[[List[ParamValue]], None]):
803
+ if self.system_id is None:
804
+ logging.warning("System ID not set, not requesting parameters")
805
+ return
806
+
807
+ if self.is_requesting_parameters:
808
+ logging.warning("Already requesting parameters")
809
+ return
810
+
811
+ RETRY_PERIOD_S = 0.25
812
+ params: List[ParamValue] = []
813
+ try:
814
+ self.is_requesting_parameters = True
815
+ self._send_param_request_list()
816
+ while True:
817
+ try:
818
+ self.param_result = None
819
+ async with self.param_cond:
820
+ await asyncio.wait_for(self.param_cond.wait(), RETRY_PERIOD_S)
821
+
822
+ if self.param_result is not None:
823
+ params.append(self.param_result)
824
+ if (
825
+ self.param_result.param_idx
826
+ == self.param_result.num_param - 1
827
+ ):
828
+ break
829
+ except asyncio.TimeoutError:
830
+ logging.error("Timeout waiting for parameter")
831
+ break
832
+
833
+ finally:
834
+ self.is_requesting_parameters = False
835
+ self.param_result = None
836
+
837
+ callback(params)
838
+
494
839
  def send_autopilot_stop(self):
495
840
  self.loop.call_soon_threadsafe(lambda: self._send_autopilot_stop())
496
841
 
497
842
  def _send_autopilot_stop(self):
498
- if not self.system_id:
843
+ if self.system_id is None:
499
844
  logging.warning("System ID not set, not sending STOP")
500
845
  return
501
846
  logging.info("Sending STOP")
@@ -532,7 +877,7 @@ class MavlinkThread:
532
877
  )
533
878
 
534
879
  def _send_waypoint(self, lat_deg: float, lon_deg: float, speed_mps: float):
535
- if not self.system_id:
880
+ if self.system_id is None:
536
881
  logging.warning("System ID not set, not sending waypoint")
537
882
  return
538
883
  logging.info(
@@ -559,7 +904,7 @@ class MavlinkThread:
559
904
  )
560
905
 
561
906
  def _send_loiter(self, speed_mps: float, radius_m: float, duration_s: float):
562
- if not self.system_id:
907
+ if self.system_id is None:
563
908
  logging.warning("System ID not set, not sending loiter")
564
909
  return
565
910
  logging.info(
@@ -707,7 +1052,7 @@ class MavlinkThread:
707
1052
  )
708
1053
 
709
1054
  for retry_num in range(max_retries):
710
- logging.debug(f"Set mission index: ({retry_num+1}/{max_retries})")
1055
+ logging.debug(f"Set mission index: ({retry_num + 1}/{max_retries})")
711
1056
 
712
1057
  self.ack_result = None
713
1058
  self.conn.send(cmd)
@@ -732,3 +1077,42 @@ class MavlinkThread:
732
1077
 
733
1078
  finally:
734
1079
  self.is_sending_mission = False
1080
+
1081
+ def set_attitude(
1082
+ self,
1083
+ roll_rad: float = 0,
1084
+ pitch_rad: float = 0,
1085
+ yaw_rad: float = 0,
1086
+ roll_rate_rps: float = 0,
1087
+ pitch_rate_rps: float = 0,
1088
+ yaw_rate_rps: float = 0,
1089
+ ):
1090
+ self.loop.call_soon_threadsafe(
1091
+ lambda: self._set_attitude(
1092
+ roll_rad,
1093
+ pitch_rad,
1094
+ yaw_rad,
1095
+ roll_rate_rps,
1096
+ pitch_rate_rps,
1097
+ yaw_rate_rps,
1098
+ )
1099
+ )
1100
+
1101
+ def _set_attitude(
1102
+ self,
1103
+ roll_rad: float,
1104
+ pitch_rad: float,
1105
+ yaw_rad: float,
1106
+ roll_rate_rps: float,
1107
+ pitch_rate_rps: float,
1108
+ yaw_rate_rps: float,
1109
+ ):
1110
+ self.conn.attitude_send(
1111
+ 0,
1112
+ roll_rad,
1113
+ pitch_rad,
1114
+ yaw_rad,
1115
+ roll_rate_rps,
1116
+ pitch_rate_rps,
1117
+ yaw_rate_rps,
1118
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mrtautopilot
3
- Version: 8
3
+ Version: 10
4
4
  Summary: Communicate with the MRT Autopilot Mavlink API
5
5
  Maintainer-email: Nathan Knotts <nathan@magothyrt.com>
6
6
  Project-URL: Homepage, https://www.magothyrt.com/
File without changes
File without changes
File without changes