From 15bb089c97a6333d11c31897cdbd828600767731 Mon Sep 17 00:00:00 2001 From: dbcarelli Date: Mon, 2 Apr 2018 19:58:13 +0400 Subject: [PATCH 1/8] Add skeleton code to read in MAVlink command mission from Pixhawk. --- .../utilities/mission_plan_parser.py | 58 ++++++------------- 1 file changed, 19 insertions(+), 39 deletions(-) diff --git a/anaconda_avionics/utilities/mission_plan_parser.py b/anaconda_avionics/utilities/mission_plan_parser.py index 2dc5545..e1eccc7 100644 --- a/anaconda_avionics/utilities/mission_plan_parser.py +++ b/anaconda_avionics/utilities/mission_plan_parser.py @@ -1,54 +1,34 @@ """ -This module takes in a CSV file in the command line and returns a list of Camera objects -and LandingWaypoint objects that are later used to upload and monitor data mule missions. -Eventually, this will accept MavLink commands instead-of/in-conjuction-with the CSV file. +This module takes in a vehicle object, reads in the mission and returns a list +of Camera objects and LandingWaypoint objects that are later used to upload and +monitor data mule missions. """ from anaconda_avionics.utilities import DataStation from anaconda_avionics.utilities import LandingWaypoint -# TODO: reconfigure this to accept QGroundControl mission plan and execute accordingly +# TODO: reconfigure this to accept Pixhawk mission plan and execute accordingly class MissionPlanParser(object): - __data_waypoints = None # Waypoints to be loitered over - __landing_waypoints = None # Waypoints to be flown through in landing sequence + self._autopilot = None + self._data_stations = [] + self._landing_waypoints = [] - def __init__(self, _data_waypoints, _landing_waypoints): - self.__data_waypoints = _data_waypoints - self.__landing_waypoints = _landing_waypoints + def __init__(self, _autopilot): + self._autopilot = _autopilot def extract_waypoints(self): """ - Reads CSV file and returns list of LandingWaypoint objects and list of Camera objects. + Downloads the MAVlink commadns from the pixhawk and reurns a list of + Camera and LandingWaypoint objects """ - # Raw latitude, longitude, and altitude for CAMERA TRAPS translated to - # Camera objects - number_of_data_stations = len(self.__data_waypoints) - data_stations = [] - for line in range(number_of_data_stations): - if not self.__data_waypoints[line][0].isalpha(): # Not data column descriptor - new_data_station = DataStation( # TODO: change Camera object to "DataStation" - float(self.__data_waypoints[line][0]), - float(self.__data_waypoints[line][1]), - int(self.__data_waypoints[line][2]), - str(self.__data_waypoints[line][3])) - data_stations.append(new_data_station) - - # Raw latitude, longitude, and altitude for LANDING SEQUENCE translated to - # LandingWaypoints - landing_waypoints = [] - waypoint_num = 0 - number_of_waypoints = len(self.__landing_waypoints) - for line in range(number_of_waypoints): - if not self.__landing_waypoints[line][0].isalpha(): # Not data column descriptor - waypoint_num += 1 - new_landing_waypoint = LandingWaypoint( - waypoint_num, - float(self.__landing_waypoints[line][0]), - float(self.__landing_waypoints[line][1]), - float(self.__landing_waypoints[line][2])) - landing_waypoints.append(new_landing_waypoint) - - return data_stations, landing_waypoints + # Read in list of MAVlink commands from the pixhakw. + + # Identify which commands correspond to data stations and which + # correspond to the landing sequence by checking Parameter #1 of each + # command. + + # Return the results + return self._data_stations, self._landing_waypoints From e0de53b6af8fd87ddb5663870dab74c67fc7e6de Mon Sep 17 00:00:00 2001 From: dbcarelli Date: Wed, 11 Apr 2018 17:52:01 +0400 Subject: [PATCH 2/8] Add RESET_ID command --- anaconda_avionics/utilities/xbee.py | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/anaconda_avionics/utilities/xbee.py b/anaconda_avionics/utilities/xbee.py index 1e5011f..029d45d 100644 --- a/anaconda_avionics/utilities/xbee.py +++ b/anaconda_avionics/utilities/xbee.py @@ -19,7 +19,7 @@ def __init__(self, serial_port="/dev/ttyUSB0"): while True: try: if not "DEVELOPMENT" in os.environ: # Don't connect to XBee while in development - self.xbee_port = serial.Serial(serial_port, 9600, timeout=5) + self.xbee_port = serial.Serial(serial_port, 57600, timeout=5) logging.info("Connected to XBee") else: logging.info("In development mode, not connecting to XBee") @@ -32,12 +32,14 @@ def __init__(self, serial_port="/dev/ttyUSB0"): self.encode = { 'POWER_ON' : '1', 'POWER_OFF' : '2', - 'EXTEND_TIME' : '3' + 'EXTEND_TIME' : '3', + 'RESET_ID' : '4' } self.decode = { '1' : 'POWER_ON', '2' : 'POWER_OFF', - '3' : 'EXTEND_TIME' + '3' : 'EXTEND_TIME', + '4' : 'RESET_ID', } self.data_station_idens = self.read_iden_map() @@ -63,6 +65,10 @@ def send_command(self, identity, command): logging.debug("xBee port write: %s" % self.encode[command]) self.xbee_port.write(self.encode[command]) + def change_id(self, identity, new_id): + self.send_command(identity, 'RESET_ID') + self.xbee_port.write('<'+new_id+'>') + def acknowledge(self, identity, command): """ Called after command is sent @@ -108,3 +114,20 @@ def acknowledge(self, identity, command): identity_index = 0 return False # Unsuccessful ACK + +if __name__ == "__main__": + serial_port = raw_input("Enter serial port: ") + xbee = XBee(serial_port) + while True: + try: + command = raw_input("Enter Command \nPOWER_ON: 1\nPOWER_OFF: 2\nEXTEND_TIME: 3\nRESET_ID: 4\nCommand: ") + try: + if (command == '4'): + new_id = raw_input("New ID: ") + xbee.change_id('street_cat', new_id) + else: + xbee.send_command('street_cat', xbee.decode[command]) + except KeyError: + pass + except KeyboardInterrupt: + xbee.xbee_port.close() From 5535219d89962e970e0dca12950106b3a62d5483 Mon Sep 17 00:00:00 2001 From: dbcarelli Date: Wed, 11 Apr 2018 22:14:22 +0400 Subject: [PATCH 3/8] Add ability to read cmds from drone and parse into list of data stations and landing waypoints. --- .../utilities/mission_plan_parser.py | 66 +++++++++++++++---- 1 file changed, 55 insertions(+), 11 deletions(-) diff --git a/anaconda_avionics/utilities/mission_plan_parser.py b/anaconda_avionics/utilities/mission_plan_parser.py index e1eccc7..515efae 100644 --- a/anaconda_avionics/utilities/mission_plan_parser.py +++ b/anaconda_avionics/utilities/mission_plan_parser.py @@ -4,19 +4,23 @@ monitor data mule missions. """ -from anaconda_avionics.utilities import DataStation -from anaconda_avionics.utilities import LandingWaypoint +#from anaconda_avionics.utilities. +from data_station import DataStation +#from anaconda_avionics.utilities. +from landing_waypoint import LandingWaypoint - -# TODO: reconfigure this to accept Pixhawk mission plan and execute accordingly class MissionPlanParser(object): - self._autopilot = None - self._data_stations = [] - self._landing_waypoints = [] - def __init__(self, _autopilot): self._autopilot = _autopilot + self._data_stations = [] + self._landing_waypoints = [] + + # load the commands + self._cmds = self._autopilot.commands + self._cmds.download() + self._cmds.wait_ready() + self._number_of_commands = self._cmds.count() def extract_waypoints(self): """ @@ -25,10 +29,50 @@ def extract_waypoints(self): """ # Read in list of MAVlink commands from the pixhakw. + cmd_index = 2 + # command 0 is always Mission Start + # command 1 is always Takeoff + while (cmd_index < self._number_of_commands): + # TODO: distinguish between landing, takeoff and obstacle avoidance + + # get the command ID + cmd = self._vehicle._wploader.wp(cmd_index) + + # determine if the command indicates a data station + if (cmd.command == 16): # 16 corresponds to MAV_CMD_NAV_WAYPOINT + new_waypoint = LandingWaypoint(cmd_index, cmd.x, cmd.y, cmd.z) + self._landing_waypoints.append(new_waypoint) + + elif (cmd.command == 189): # 189 corresponds to DO_LAND_START + new_waypoint = LandingWaypoint(cmd_index, cmd.x, cmd.y, cmd.z) + self._landing_waypoints.append(new_waypoint) - # Identify which commands correspond to data stations and which - # correspond to the landing sequence by checking Parameter #1 of each - # command. + elif (cmd.command == 12): # 21 corresponds to MAV_CMD_NAV_LAND + new_waypoint = LandingWaypoint(cmd_index, cmd.x, cmd.y, cmd.z) + self._landing_waypoints.append(new_waypoint) + + elif (cmd.command == 31010): # corresponds to a data station! + new_data_station = DataStaion(cmd.x, cmd.y, cmd.z, cmd.param1) + self._data_stations.append(new_data_station) + # the next waypoint is MAV_CMD_NAV_WAYPOINT + # at the same location + # TODO: add check to make sure the above is true + cmd_index += 1 + + else: + # we should throw some king of global error for cmds that + # the drone is not expecting + pass # Return the results return self._data_stations, self._landing_waypoints + +if __name__ == '__main__': + from dronekit import connect, VehicleMode, LocationGlobalRelative, LocationGlobal, Command + + connection_string = raw_input("Enter connection string: ") + print 'Connecting to vehicle on: %s' % connection_string + vehicle = connect(connection_string, baud=57600, wait_ready=True) + print 'Connection succesful!' + + parser = MissionPlanParser(vehicle) From ca951c714184f1d6e4d8ed807f4cbab8dc6cb71c Mon Sep 17 00:00:00 2001 From: dbcarelli Date: Mon, 16 Apr 2018 23:43:03 +0400 Subject: [PATCH 4/8] Bug fix. --- .../utilities/mission_plan_parser.py | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/anaconda_avionics/utilities/mission_plan_parser.py b/anaconda_avionics/utilities/mission_plan_parser.py index 515efae..7907360 100644 --- a/anaconda_avionics/utilities/mission_plan_parser.py +++ b/anaconda_avionics/utilities/mission_plan_parser.py @@ -20,7 +20,7 @@ def __init__(self, _autopilot): self._cmds = self._autopilot.commands self._cmds.download() self._cmds.wait_ready() - self._number_of_commands = self._cmds.count() + self._number_of_commands = self._cmds.count def extract_waypoints(self): """ @@ -29,15 +29,17 @@ def extract_waypoints(self): """ # Read in list of MAVlink commands from the pixhakw. - cmd_index = 2 + cmd_index = 1 # command 0 is always Mission Start # command 1 is always Takeoff while (cmd_index < self._number_of_commands): # TODO: distinguish between landing, takeoff and obstacle avoidance # get the command ID - cmd = self._vehicle._wploader.wp(cmd_index) + + cmd = self._autopilot._wploader.wp(cmd_index) + print cmd.command # determine if the command indicates a data station if (cmd.command == 16): # 16 corresponds to MAV_CMD_NAV_WAYPOINT new_waypoint = LandingWaypoint(cmd_index, cmd.x, cmd.y, cmd.z) @@ -51,8 +53,8 @@ def extract_waypoints(self): new_waypoint = LandingWaypoint(cmd_index, cmd.x, cmd.y, cmd.z) self._landing_waypoints.append(new_waypoint) - elif (cmd.command == 31010): # corresponds to a data station! - new_data_station = DataStaion(cmd.x, cmd.y, cmd.z, cmd.param1) + elif (cmd.command == 201): # corresponds to a data station! MAV_CMD_DO_SET_ROI + new_data_station = DataStation(cmd.x, cmd.y, cmd.z, cmd.param4) self._data_stations.append(new_data_station) # the next waypoint is MAV_CMD_NAV_WAYPOINT # at the same location @@ -64,6 +66,8 @@ def extract_waypoints(self): # the drone is not expecting pass + cmd_index += 1 + # Return the results return self._data_stations, self._landing_waypoints @@ -76,3 +80,18 @@ def extract_waypoints(self): print 'Connection succesful!' parser = MissionPlanParser(vehicle) + parser.extract_waypoints() + + print "Landing Waypoints: " + for waypoint in parser._landing_waypoints: + print "Lat: ", waypoint.lat, "Lon: ", waypoint.lon, "Alt: ", waypoint.alt + + print "\n----------------------\n" + + for ds in parser._data_stations: + print "Data Station: ", ds.identity + print " Lat: ", ds.lat, "Lon: ", ds.lon, "Alt: ", ds.alt + print " Timeout Event: ", ds.timeout + print " Drone Arrived: ", ds.drone_arrived + print " Download Start: ", ds.download_started + print " Download Complete: ", ds.download_complete From d452bcf5edbe294a9a0e5305014561b0baa76383 Mon Sep 17 00:00:00 2001 From: dbcarelli Date: Mon, 23 Apr 2018 19:01:02 +0400 Subject: [PATCH 5/8] Add option to request GPS data from data station --- anaconda_avionics/utilities/xbee.py | 43 +++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/anaconda_avionics/utilities/xbee.py b/anaconda_avionics/utilities/xbee.py index 029d45d..7893236 100644 --- a/anaconda_avionics/utilities/xbee.py +++ b/anaconda_avionics/utilities/xbee.py @@ -12,6 +12,7 @@ def __init__(self, serial_port="/dev/ttyUSB0"): self.encode = None self.decode = None self.data_station_idens = None + self.timeout = 360 # seconds self.preamble_out = ['s', 't', 'r', 'e', 'e', 't'] self.preamble_in = ['c', 'a', 't'] @@ -30,16 +31,18 @@ def __init__(self, serial_port="/dev/ttyUSB0"): # TODO: make single dictionary self.encode = { - 'POWER_ON' : '1', - 'POWER_OFF' : '2', - 'EXTEND_TIME' : '3', - 'RESET_ID' : '4' + 'POWER_ON' : '1', + 'POWER_OFF' : '2', + 'EXTEND_TIME' : '3', + 'RESET_ID' : '4', + 'REQUEST_GPS' : '5', } self.decode = { '1' : 'POWER_ON', '2' : 'POWER_OFF', '3' : 'EXTEND_TIME', '4' : 'RESET_ID', + '5' : 'REQUEST_GPS', } self.data_station_idens = self.read_iden_map() @@ -69,6 +72,34 @@ def change_id(self, identity, new_id): self.send_command(identity, 'RESET_ID') self.xbee_port.write('<'+new_id+'>') + def get_gps_data(self, identity): + self.send_command(identity, 'REQUEST_GPS') + time_start = time.time() + incoming_byte = '' + prelimitor_received = False + postlmitor_received = False + while True: + if (self.xbee_port.in_waiting > 0): + if(self.xbee_port.read() == '<'): + break + if ((time.time() - time_start) > self.timeout): + return '-1' + time.sleep(0.1) + + gps_coordinates = '' + while True: + if (self.xbee_port.in_waiting > 0): + incoming_byte = self.xbee_port.read() + + if (incoming_byte == '>'): + return gps_coordinates + else: + gps_coordinates += incoming_byte + + if ((time.time() - time_start) > self.timeout): + return '-1' + time.sleep(0.1) + def acknowledge(self, identity, command): """ Called after command is sent @@ -120,11 +151,13 @@ def acknowledge(self, identity, command): xbee = XBee(serial_port) while True: try: - command = raw_input("Enter Command \nPOWER_ON: 1\nPOWER_OFF: 2\nEXTEND_TIME: 3\nRESET_ID: 4\nCommand: ") + command = raw_input("Enter Command \nPOWER_ON: 1\nPOWER_OFF: 2\nEXTEND_TIME: 3\nRESET_ID: 4\nREQUEST_GPS: 5\nCommand: ") try: if (command == '4'): new_id = raw_input("New ID: ") xbee.change_id('street_cat', new_id) + elif (command == '5'): + print xbee.get_gps_data('street_cat') else: xbee.send_command('street_cat', xbee.decode[command]) except KeyError: From 22137f825b3e19f1210a7c6196f821ca56f0b569 Mon Sep 17 00:00:00 2001 From: dbcarelli Date: Wed, 25 Apr 2018 20:01:20 +0400 Subject: [PATCH 6/8] Move tests folder. Add test for mission plan parser --- anaconda_avionics/tests/__init__.py | 0 .../tests/test_mission_plan_parser.py | 56 +++++++++++++ .../tests/test_mission_preparation.py | 0 anaconda_avionics/tests/test_navigation.py | 0 anaconda_avionics/tests/test_sftp_client.py | 15 ++++ anaconda_avionics/tests/test_xbee.py | 14 ++++ anaconda_avionics/tests/unit_tests.py | 79 +++++++++++++++++++ 7 files changed, 164 insertions(+) create mode 100644 anaconda_avionics/tests/__init__.py create mode 100644 anaconda_avionics/tests/test_mission_plan_parser.py create mode 100644 anaconda_avionics/tests/test_mission_preparation.py create mode 100644 anaconda_avionics/tests/test_navigation.py create mode 100644 anaconda_avionics/tests/test_sftp_client.py create mode 100644 anaconda_avionics/tests/test_xbee.py create mode 100644 anaconda_avionics/tests/unit_tests.py diff --git a/anaconda_avionics/tests/__init__.py b/anaconda_avionics/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/anaconda_avionics/tests/test_mission_plan_parser.py b/anaconda_avionics/tests/test_mission_plan_parser.py new file mode 100644 index 0000000..161952d --- /dev/null +++ b/anaconda_avionics/tests/test_mission_plan_parser.py @@ -0,0 +1,56 @@ +""" +This test will confirm the functionality of the system starting from QGS upload +to drone download and parsing. The flow of the test will be as follows: + 1) Load the unit test data station in Mission Mule's QGroundControl. + 2) Load the unit test landing sequance in Mission Mule's QGroundControl. + 3) Auto-Generate the mission and upload to the drone. + 4) From the anaconda_avionics directory, run + python -m test_mission_plan_parser +""" + +from utilities.mission_plan_parser import MissionPlanParser +from utilities.data_station import DataStation +from utilities.landing_waypoint import LandingWaypoint +from dronekit import connect + +connection_string = "/dev/ttyS0" + + + +if __name__ == '__main__': + + # create the expected values + expected_data_stations = [] + expected_data_stations.append(DataStation(24.521592, 54.435452, 60, '03')) + expected_data_stations.append(DataStation(24.522882, 54.437273, 60, '07')) + expected_data_stations.append(DataStation(24.526051, 54.432802, 60, '17')) + expected_landing_waypoints = [] + expected_landing_waypoints.append(LandingWaypoint(24.527945, 54.430411, 50)) + expected_landing_waypoints.append(LandingWaypoint(24.527945, 54.430411, 50)) + expected_landing_waypoints.append(LandingWaypoint(24.523096, 54.433605, 50)) + + + # connect to vehicle + vehicle = connect(connection_string, baud=57600, wait_ready=True) + + # instatiate MissionPlanParser object + mission_plan_parser = MissionPlanParser(vehicle) + + # extract waypoints + data_stations, landing_waypoints = mission_plan_parser.extract_waypoints() + + data_stations_test_passed = True + for i in range(3): + data_stations_test_passed &= expected_data_stations[i].lat = data_stations[i].lat + data_stations_test_passed &= expected_data_stations[i].lon = data_stations[i].lon + data_stations_test_passed &= expected_data_stations[i].alt = data_stations[i].alt + data_stations_test_passed &= expected_data_stations[i].id = data_stations[i].id + + landing_sequence_test_passed = True + for i in range(3): + landing_sequence_test_passed &= expected_landing_waypoints[i].lat = landing_waypoints[i].lat + landing_sequence_test_passed &= expected_landing_waypoints[i].lon = landing_waypoints[i].lon + landing_sequence_test_passed &= expected_landing_waypoints[i].alt = landing_waypoints[i].alt + + print "Data Station Test Passed: ", data_stations_test_passed + print "Landing Sequence Test Passed: ", landing_sequence_test_passed diff --git a/anaconda_avionics/tests/test_mission_preparation.py b/anaconda_avionics/tests/test_mission_preparation.py new file mode 100644 index 0000000..e69de29 diff --git a/anaconda_avionics/tests/test_navigation.py b/anaconda_avionics/tests/test_navigation.py new file mode 100644 index 0000000..e69de29 diff --git a/anaconda_avionics/tests/test_sftp_client.py b/anaconda_avionics/tests/test_sftp_client.py new file mode 100644 index 0000000..818b94f --- /dev/null +++ b/anaconda_avionics/tests/test_sftp_client.py @@ -0,0 +1,15 @@ +import unittest +from anaconda_avionics.utilities import SFTPClient + +class SFTPClientTest(unittest.TestCase): + """SFTP download test.""" + + def test_sftp_download(self): + sftp = SFTPClient('pi', 'raspberry', 'cameratrap.local') + + sftp.downloadAndDeleteAllFiles() + + sftp.close() + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/anaconda_avionics/tests/test_xbee.py b/anaconda_avionics/tests/test_xbee.py new file mode 100644 index 0000000..f1bd078 --- /dev/null +++ b/anaconda_avionics/tests/test_xbee.py @@ -0,0 +1,14 @@ +import time +import logging + +from anaconda_avionics.utilities.xbee import XBee + +# TODO: Make this a proper HITL test on naked drone system +def test_xbee(): + xBee = XBee(serial_port="/dev/cu.usbserial-DN00OLOU") + while True: + xBee.send_command('street_cat', 'POWER_ON') + if (xBee.acknowledge('street_cat', 'POWER_ON')): + logging.debug("Success") + break + time.sleep(0.5) \ No newline at end of file diff --git a/anaconda_avionics/tests/unit_tests.py b/anaconda_avionics/tests/unit_tests.py new file mode 100644 index 0000000..a0375a5 --- /dev/null +++ b/anaconda_avionics/tests/unit_tests.py @@ -0,0 +1,79 @@ +""" +Unit tests to verify partial functionality of data mule system. +""" + +import threading +from Queue import Queue + +import anaconda_avionics.plane_navigation_script +from anaconda_avionics.utilities import mission_plan_parser +from anaconda_avionics.utilities.xbee import XBee + + +def unit_test_csv_parser(): + """ + This unit test only extracts camera trap locations and landing + sequence data and converts them into the appropriate objects. + """ + camera_traps, landing_waypoints = mission_plan_parser.extract_waypoints() + + for camera in camera_traps: + print camera.summary + + for waypoint in landing_waypoints: + print waypoint.summary + + +def unit_test_prepare_mission(): + """ + This unit test stops after uploading the mission to the pixhawk. + """ + camera_traps, landing_waypoints = mission_plan_parser.extract_waypoints() + mission_queue = Queue() + mission_queue.put(camera_traps) + + message_queue = Queue() + logger = Logger('Unit Test:', 'prepare_mission', message_queue) + + logger_thread = threading.Thread(target=logger.start_logging, args=()) + logger_thread.start() + + anaconda_avionics.plane_navigation_script.prepare_mission(mission_queue, landing_waypoints, message_queue) + + message_queue.put('mission_end') + +def unit_test_navigation(): + """ + This unit test runs throught a full data mule mission without actually + trying to download data from camer traps. + """ + camera_traps, landing_waypoints = mission_plan_parser.extract_waypoints() + mission_queue = Queue() + mission_queue.put(camera_traps) + + message_queue = Queue() + logger = Logger('Unit Test:', 'prepare_mission', message_queue) + + logger_thread = threading.Thread(target=logger.start_logging, args=()) + logger_thread.start() + + anaconda_avionics.plane_navigation_script.navigation(mission_queue, landing_waypoints, message_queue) + +def unit_test_xbee_comm(iden_num): + """ + This unit test has two funtions: + * if identity = '?', then it will query the camera trap for its ID and print + this value + * otherwise, the xBee will turn on and off a camera trap of given ID + """ + dummy_queue = Queue() + xbee = XBee(dummy_queue) + + if iden_num == '?': + response = xbee.sendCommand('Identify') + print "Camera ID: %s" % response.message + + else: + xbee.sendCommand('Power On', idenity=iden_num, timeout=30) + time.sleep(30) + xbee.sendCommand('Power Off', iden=iden_num, timeout=30) From 2581db5765d5f254c6e947b64622940f05d8616f Mon Sep 17 00:00:00 2001 From: dbcarelli Date: Wed, 25 Apr 2018 20:04:32 +0400 Subject: [PATCH 7/8] mission plan parser can recognize MAV_CMD_NAV_LOITER_TO_ALT --- anaconda_avionics/utilities/mission_plan_parser.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/anaconda_avionics/utilities/mission_plan_parser.py b/anaconda_avionics/utilities/mission_plan_parser.py index 7907360..141b72d 100644 --- a/anaconda_avionics/utilities/mission_plan_parser.py +++ b/anaconda_avionics/utilities/mission_plan_parser.py @@ -53,6 +53,10 @@ def extract_waypoints(self): new_waypoint = LandingWaypoint(cmd_index, cmd.x, cmd.y, cmd.z) self._landing_waypoints.append(new_waypoint) + elif (cmd.command == 31): # 31 corresponds to MAV_CMD_NAV_LOITER_TO_ALT + new_waypoint = LandingWaypoint(cmd_index, cmd.x, cmd.y, cmd.z) + self._landing_waypoints.append(new_waypoint) + elif (cmd.command == 201): # corresponds to a data station! MAV_CMD_DO_SET_ROI new_data_station = DataStation(cmd.x, cmd.y, cmd.z, cmd.param4) self._data_stations.append(new_data_station) From aa77ac2b8e18b710eabbb3364238264f98e2a35e Mon Sep 17 00:00:00 2001 From: dbcarelli Date: Wed, 25 Apr 2018 20:07:20 +0400 Subject: [PATCH 8/8] Delete old tests folder --- tests/__init__.py | 0 tests/test_mission_plan_parser.py | 0 tests/test_mission_preparation.py | 0 tests/test_navigation.py | 0 tests/test_sftp_client.py | 15 ------ tests/test_xbee.py | 14 ------ tests/unit_tests.py | 79 ------------------------------- 7 files changed, 108 deletions(-) delete mode 100644 tests/__init__.py delete mode 100644 tests/test_mission_plan_parser.py delete mode 100644 tests/test_mission_preparation.py delete mode 100644 tests/test_navigation.py delete mode 100644 tests/test_sftp_client.py delete mode 100644 tests/test_xbee.py delete mode 100644 tests/unit_tests.py diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/test_mission_plan_parser.py b/tests/test_mission_plan_parser.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/test_mission_preparation.py b/tests/test_mission_preparation.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/test_navigation.py b/tests/test_navigation.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/test_sftp_client.py b/tests/test_sftp_client.py deleted file mode 100644 index 818b94f..0000000 --- a/tests/test_sftp_client.py +++ /dev/null @@ -1,15 +0,0 @@ -import unittest -from anaconda_avionics.utilities import SFTPClient - -class SFTPClientTest(unittest.TestCase): - """SFTP download test.""" - - def test_sftp_download(self): - sftp = SFTPClient('pi', 'raspberry', 'cameratrap.local') - - sftp.downloadAndDeleteAllFiles() - - sftp.close() - -if __name__ == '__main__': - unittest.main() \ No newline at end of file diff --git a/tests/test_xbee.py b/tests/test_xbee.py deleted file mode 100644 index f1bd078..0000000 --- a/tests/test_xbee.py +++ /dev/null @@ -1,14 +0,0 @@ -import time -import logging - -from anaconda_avionics.utilities.xbee import XBee - -# TODO: Make this a proper HITL test on naked drone system -def test_xbee(): - xBee = XBee(serial_port="/dev/cu.usbserial-DN00OLOU") - while True: - xBee.send_command('street_cat', 'POWER_ON') - if (xBee.acknowledge('street_cat', 'POWER_ON')): - logging.debug("Success") - break - time.sleep(0.5) \ No newline at end of file diff --git a/tests/unit_tests.py b/tests/unit_tests.py deleted file mode 100644 index a0375a5..0000000 --- a/tests/unit_tests.py +++ /dev/null @@ -1,79 +0,0 @@ -""" -Unit tests to verify partial functionality of data mule system. -""" - -import threading -from Queue import Queue - -import anaconda_avionics.plane_navigation_script -from anaconda_avionics.utilities import mission_plan_parser -from anaconda_avionics.utilities.xbee import XBee - - -def unit_test_csv_parser(): - """ - This unit test only extracts camera trap locations and landing - sequence data and converts them into the appropriate objects. - """ - camera_traps, landing_waypoints = mission_plan_parser.extract_waypoints() - - for camera in camera_traps: - print camera.summary - - for waypoint in landing_waypoints: - print waypoint.summary - - -def unit_test_prepare_mission(): - """ - This unit test stops after uploading the mission to the pixhawk. - """ - camera_traps, landing_waypoints = mission_plan_parser.extract_waypoints() - mission_queue = Queue() - mission_queue.put(camera_traps) - - message_queue = Queue() - logger = Logger('Unit Test:', 'prepare_mission', message_queue) - - logger_thread = threading.Thread(target=logger.start_logging, args=()) - logger_thread.start() - - anaconda_avionics.plane_navigation_script.prepare_mission(mission_queue, landing_waypoints, message_queue) - - message_queue.put('mission_end') - -def unit_test_navigation(): - """ - This unit test runs throught a full data mule mission without actually - trying to download data from camer traps. - """ - camera_traps, landing_waypoints = mission_plan_parser.extract_waypoints() - mission_queue = Queue() - mission_queue.put(camera_traps) - - message_queue = Queue() - logger = Logger('Unit Test:', 'prepare_mission', message_queue) - - logger_thread = threading.Thread(target=logger.start_logging, args=()) - logger_thread.start() - - anaconda_avionics.plane_navigation_script.navigation(mission_queue, landing_waypoints, message_queue) - -def unit_test_xbee_comm(iden_num): - """ - This unit test has two funtions: - * if identity = '?', then it will query the camera trap for its ID and print - this value - * otherwise, the xBee will turn on and off a camera trap of given ID - """ - dummy_queue = Queue() - xbee = XBee(dummy_queue) - - if iden_num == '?': - response = xbee.sendCommand('Identify') - print "Camera ID: %s" % response.message - - else: - xbee.sendCommand('Power On', idenity=iden_num, timeout=30) - time.sleep(30) - xbee.sendCommand('Power Off', iden=iden_num, timeout=30)