Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Path: blob/master/Tools/autotest/ardusub.py
Views: 1798
'''1Dive ArduSub in SITL23Depth of water is 50m, the ground is flat4Parameters are in-code defaults plus default_params/sub.parm56AP_FLAKE8_CLEAN7'''89from __future__ import print_function10import os11import sys1213from pymavlink import mavutil1415import vehicle_test_suite16from vehicle_test_suite import NotAchievedException17from vehicle_test_suite import AutoTestTimeoutException18from vehicle_test_suite import PreconditionFailedException1920if sys.version_info[0] < 3:21ConnectionResetError = AutoTestTimeoutException2223# get location of scripts24testdir = os.path.dirname(os.path.realpath(__file__))2526SITL_START_LOCATION = mavutil.location(33.810313, -118.393867, 0, 185)272829class Joystick():30Pitch = 131Roll = 232Throttle = 333Yaw = 434Forward = 535Lateral = 6363738# Values for EK3_MAG_CAL39class MagCal():40WHEN_FLYING = 041WHEN_MANOEUVRING = 142NEVER = 243AFTER_FIRST_CLIMB = 344ALWAYS = 4454647# Values for XKFS.MAG_FUSION48class MagFuseSel():49NOT_FUSING = 050FUSE_YAW = 151FUSE_MAG = 2525354class AutoTestSub(vehicle_test_suite.TestSuite):55@staticmethod56def get_not_armable_mode_list():57return []5859@staticmethod60def get_not_disarmed_settable_modes_list():61return []6263@staticmethod64def get_no_position_not_settable_modes_list():65return ["AUTO", "GUIDED", "CIRCLE", "POSHOLD"]6667@staticmethod68def get_position_armable_modes_list():69return []7071@staticmethod72def get_normal_armable_modes_list():73return ["ACRO", "ALT_HOLD", "MANUAL", "STABILIZE", "SURFACE"]7475def log_name(self):76return "ArduSub"7778def default_speedup(self):79'''Sub seems to be race-free'''80return 1008182def test_filepath(self):83return os.path.realpath(__file__)8485def set_current_test_name(self, name):86self.current_test_name_directory = "ArduSub_Tests/" + name + "/"8788def default_mode(self):89return 'MANUAL'9091def sitl_start_location(self):92return SITL_START_LOCATION9394def default_frame(self):95return 'vectored'9697def is_sub(self):98return True99100def watch_altitude_maintained(self, delta=0.3, timeout=5.0):101"""Watch and wait for the actual altitude to be maintained102103Keyword Arguments:104delta {float} -- Maximum altitude range to be allowed from actual point (default: {0.5})105timeout {float} -- Timeout time in simulation seconds (default: {5.0})106107Raises:108NotAchievedException: Exception when altitude fails to hold inside the time and109altitude range110"""111tstart = self.get_sim_time_cached()112previous_altitude = self.mav.recv_match(type='VFR_HUD', blocking=True).alt113self.progress('Altitude to be watched: %f' % (previous_altitude))114while True:115m = self.mav.recv_match(type='VFR_HUD', blocking=True)116if self.get_sim_time_cached() - tstart > timeout:117self.progress('Altitude hold done: %f' % (previous_altitude))118return119if abs(m.alt - previous_altitude) > delta:120raise NotAchievedException(121"Altitude not maintained: want %.2f (+/- %.2f) got=%.2f" %122(previous_altitude, delta, m.alt))123124def AltitudeHold(self):125"""Test ALT_HOLD mode"""126self.wait_ready_to_arm()127self.arm_vehicle()128self.change_mode('ALT_HOLD')129130msg = self.mav.recv_match(type='GLOBAL_POSITION_INT', blocking=True, timeout=5)131if msg is None:132raise NotAchievedException("Did not get GLOBAL_POSITION_INT")133pwm = 1300134if msg.relative_alt/1000.0 < -6.0:135# need to go up, not down!136pwm = 1700137self.set_rc(Joystick.Throttle, pwm)138self.wait_altitude(altitude_min=-6, altitude_max=-5)139self.set_rc(Joystick.Throttle, 1500)140141# let the vehicle settle (momentum / stopping point shenanigans....)142self.delay_sim_time(1)143144self.watch_altitude_maintained()145146self.set_rc(Joystick.Throttle, 1000)147self.wait_altitude(altitude_min=-20, altitude_max=-19)148self.set_rc(Joystick.Throttle, 1500)149150# let the vehicle settle (momentum / stopping point shenanigans....)151self.delay_sim_time(1)152153self.watch_altitude_maintained()154155self.set_rc(Joystick.Throttle, 1900)156self.wait_altitude(altitude_min=-14, altitude_max=-13)157self.set_rc(Joystick.Throttle, 1500)158159# let the vehicle settle (momentum / stopping point shenanigans....)160self.delay_sim_time(1)161162self.watch_altitude_maintained()163164self.set_rc(Joystick.Throttle, 1900)165self.wait_altitude(altitude_min=-5, altitude_max=-4)166self.set_rc(Joystick.Throttle, 1500)167168# let the vehicle settle (momentum / stopping point shenanigans....)169self.delay_sim_time(1)170self.watch_altitude_maintained()171172# Make sure the code can handle buoyancy changes173self.set_parameter("SIM_BUOYANCY", 10)174self.watch_altitude_maintained()175self.set_parameter("SIM_BUOYANCY", -10)176self.watch_altitude_maintained()177178# Make sure that the ROV will dive with a small input down even if there is a 10N buoyancy force upwards179self.set_parameter("SIM_BUOYANCY", 10)180self.set_rc(Joystick.Throttle, 1350)181self.wait_altitude(altitude_min=-6, altitude_max=-5.5)182183self.set_rc(Joystick.Throttle, 1500)184self.watch_altitude_maintained()185self.disarm_vehicle()186187def RngfndQuality(self):188"""Check lua Range Finder quality information flow"""189self.context_push()190self.context_collect('STATUSTEXT')191192ex = None193try:194self.set_parameters({195"SCR_ENABLE": 1,196"RNGFND1_TYPE": 36,197"RNGFND1_ORIENT": 25,198"RNGFND1_MIN_CM": 10,199"RNGFND1_MAX_CM": 5000,200})201202self.install_example_script_context("rangefinder_quality_test.lua")203204# These string must match those sent by the lua test script.205complete_str = "#complete#"206failure_str = "!!failure!!"207208self.reboot_sitl()209210self.wait_statustext(complete_str, timeout=20, check_context=True)211found_failure = self.statustext_in_collections(failure_str)212213if found_failure is not None:214raise NotAchievedException("RngfndQuality test failed: " + found_failure.text)215216except Exception as e:217self.print_exception_caught(e)218ex = e219220self.context_pop()221222# restart SITL RF driver223self.reboot_sitl()224225if ex:226raise ex227228def watch_distance_maintained(self, delta=0.3, timeout=5.0):229"""Watch and wait for the rangefinder reading to be maintained"""230tstart = self.get_sim_time_cached()231previous_distance = self.mav.recv_match(type='RANGEFINDER', blocking=True).distance232self.progress('Distance to be watched: %.2f' % previous_distance)233while True:234m = self.mav.recv_match(type='RANGEFINDER', blocking=True)235if self.get_sim_time_cached() - tstart > timeout:236self.progress('Distance hold done: %f' % previous_distance)237return238if abs(m.distance - previous_distance) > delta:239raise NotAchievedException(240"Distance not maintained: want %.2f (+/- %.2f) got=%.2f" %241(previous_distance, delta, m.distance))242243def Surftrak(self):244"""Test SURFTRAK mode"""245246if self.get_parameter('RNGFND1_MAX_CM') != 3000.0:247raise PreconditionFailedException("RNGFND1_MAX_CM is not %g" % 3000.0)248249# Something closer to Bar30 noise250self.context_push()251self.set_parameter("SIM_BARO_RND", 0.01)252253self.wait_ready_to_arm()254self.arm_vehicle()255self.change_mode('MANUAL')256257# Dive to -5m, outside of rangefinder range, will act like ALT_HOLD258pwm = 1300 if self.get_altitude(relative=True) > -6 else 1700259self.set_rc(Joystick.Throttle, pwm)260self.wait_altitude(altitude_min=-6, altitude_max=-5, relative=False, timeout=60)261self.set_rc(Joystick.Throttle, 1500)262self.delay_sim_time(1)263self.context_collect('STATUSTEXT')264self.change_mode(21)265self.wait_statustext('waiting for a rangefinder reading', check_context=True)266self.context_clear_collection('STATUSTEXT')267self.watch_altitude_maintained()268269# Move into range, should set a rangefinder target and maintain it270self.set_rc(Joystick.Throttle, 1300)271self.wait_altitude(altitude_min=-26, altitude_max=-25, relative=False, timeout=60)272self.set_rc(Joystick.Throttle, 1500)273self.delay_sim_time(4)274self.wait_statustext('rangefinder target is', check_context=True)275self.context_clear_collection('STATUSTEXT')276self.watch_distance_maintained()277278# Move a few meters, should apply a delta and maintain the new rangefinder target279self.set_rc(Joystick.Throttle, 1300)280self.wait_altitude(altitude_min=-31, altitude_max=-30, relative=False, timeout=60)281self.set_rc(Joystick.Throttle, 1500)282self.delay_sim_time(4)283self.wait_statustext('rangefinder target is', check_context=True)284self.watch_distance_maintained()285286self.disarm_vehicle()287self.context_pop()288289def prepare_synthetic_seafloor_test(self, sea_floor_depth, rf_target):290self.set_parameters({291"SCR_ENABLE": 1,292"RNGFND1_TYPE": 36,293"RNGFND1_ORIENT": 25,294"RNGFND1_MIN_CM": 10,295"RNGFND1_MAX_CM": 3000,296"SCR_USER1": 2, # Configuration bundle297"SCR_USER2": sea_floor_depth, # Depth in meters298"SCR_USER3": 101, # Output log records299"SCR_USER4": rf_target, # Rangefinder target in meters300})301302self.install_example_script_context("sub_test_synthetic_seafloor.lua")303304# Reboot to enable scripting.305self.reboot_sitl()306self.set_rc_default()307self.wait_ready_to_arm()308309def watch_true_distance_maintained(self, match_distance, delta=0.3, timeout=5.0, final_waypoint=0):310"""Watch and wait for the rangefinder reading to be maintained"""311312def get_true_distance():313"""Return the True distance from the simulated range finder"""314m_true = self.mav.recv_match(type='STATUSTEXT', blocking=True, timeout=3.0)315if m_true is None:316return m_true317idx_tr = m_true.text.find('#TR#')318if idx_tr < 0:319return None320return float(m_true.text[(idx_tr+4):(idx_tr+12)])321322tstart = self.get_sim_time_cached()323self.progress('Distance to be watched: %.2f (+/- %.2f)' % (match_distance, delta))324max_delta = 0.0325326while True:327timed_out = self.get_sim_time_cached() - tstart > timeout328# If final_waypoint>0 then timeout is failure, otherwise success329if timed_out and final_waypoint > 0:330raise NotAchievedException(331"Mission not complete: want waypoint %i, only made it to waypoint %i" %332(final_waypoint, self.mav.waypoint_current()))333if timed_out:334self.progress('Distance hold done. Max delta:%.2fm' % max_delta)335return336337true_distance = get_true_distance()338if true_distance is None:339continue340match_delta = abs(true_distance - match_distance)341if match_delta > max_delta:342max_delta = match_delta343if match_delta > delta:344raise NotAchievedException(345"Distance not maintained: want %.2f (+/- %.2f) got=%.2f (%.2f)" %346(match_distance, delta, true_distance, match_delta))347if final_waypoint > 0:348if self.mav.waypoint_current() >= final_waypoint:349self.progress('Distance hold during mission done. Max delta:%.2fm' % max_delta)350return351352def SimTerrainSurftrak(self):353"""Move at a constant height above synthetic sea floor"""354355sea_floor_depth = 50 # Depth of sea floor at location of test356match_distance = 15 # Desired sub distance from sea floor357start_altitude = -sea_floor_depth+match_distance358end_altitude = start_altitude - 10359validation_delta = 1.5 # Largest allowed distance between sub height and desired height360361self.context_push()362self.prepare_synthetic_seafloor_test(sea_floor_depth, match_distance)363self.change_mode('MANUAL')364self.arm_vehicle()365366# Dive to match_distance off the bottom in preparation for the mission367pwm = 1300 if self.get_altitude(relative=True) > start_altitude else 1700368self.set_rc(Joystick.Throttle, pwm)369self.wait_altitude(altitude_min=start_altitude-1, altitude_max=start_altitude, relative=False, timeout=120)370self.set_rc(Joystick.Throttle, 1500)371self.delay_sim_time(1)372373# Turn on surftrak and move around374self.change_mode(21)375376# Go south over the ridge.377self.reach_heading_manual(180)378self.set_rc(Joystick.Forward, 1650)379self.watch_true_distance_maintained(match_distance, delta=validation_delta, timeout=60)380self.set_rc(Joystick.Forward, 1500)381382# Shift west a bit383self.reach_heading_manual(270)384self.set_rc(Joystick.Forward, 1650)385self.watch_true_distance_maintained(match_distance, delta=validation_delta, timeout=5)386self.set_rc(Joystick.Forward, 1500)387388# Go south over the plateau389self.reach_heading_manual(180)390self.set_rc(Joystick.Forward, 1650)391self.watch_true_distance_maintained(match_distance, delta=validation_delta, timeout=60)392393# The mission ends at end_altitude. Do a check to ensure that the sub is at this altitude394self.wait_altitude(altitude_min=end_altitude-validation_delta/2, altitude_max=end_altitude+validation_delta/2,395relative=False, timeout=1)396397self.set_rc(Joystick.Forward, 1500)398399self.disarm_vehicle()400self.context_pop()401self.reboot_sitl() # e.g. revert rangefinder configuration402403def SimTerrainMission(self):404"""Mission at a constant height above synthetic sea floor"""405406sea_floor_depth = 50 # Depth of sea floor at location of test407match_distance = 15 # Desired sub distance from sea floor408start_altitude = -sea_floor_depth+match_distance409end_altitude = start_altitude - 10410validation_delta = 1.5 # Largest allowed distance between sub height and desired height411412self.context_push()413self.prepare_synthetic_seafloor_test(sea_floor_depth, match_distance)414415# The synthetic seafloor has an east-west ridge south of the sub.416# The mission contained in terrain_mission.txt instructs the sub417# to remain at 15m above the seafloor and travel south over the418# ridge. Then the sub moves west and travels north over the ridge.419filename = "terrain_mission.txt"420self.load_mission(filename)421422self.change_mode('MANUAL')423self.arm_vehicle()424425# Dive to match_distance off the bottom in preparation for the mission426pwm = 1300 if self.get_altitude(relative=True) > start_altitude else 1700427self.set_rc(Joystick.Throttle, pwm)428self.wait_altitude(altitude_min=start_altitude-1, altitude_max=start_altitude, relative=False, timeout=120)429self.set_rc(Joystick.Throttle, 1500)430self.delay_sim_time(1)431432self.change_mode('AUTO')433self.watch_true_distance_maintained(match_distance, delta=validation_delta, timeout=500.0, final_waypoint=4)434435# The mission ends at end_altitude. Do a check to ensure that the sub is at this altitude.436self.wait_altitude(altitude_min=end_altitude-validation_delta/2, altitude_max=end_altitude+validation_delta/2,437relative=False, timeout=1)438439self.disarm_vehicle()440self.context_pop()441self.reboot_sitl() # e.g. revert rangefinder configuration442443def ModeChanges(self, delta=0.2):444"""Check if alternating between ALTHOLD, STABILIZE, POSHOLD and SURFTRAK (mode 21) affects altitude"""445self.wait_ready_to_arm()446self.arm_vehicle()447# zero buoyancy and no baro noise448self.set_parameter("SIM_BUOYANCY", 0)449self.set_parameter("SIM_BARO_RND", 0)450# dive a bit to make sure we are not surfaced451self.change_mode('STABILIZE')452self.set_rc(Joystick.Throttle, 1350)453self.delay_sim_time(10)454self.set_rc(Joystick.Throttle, 1500)455self.delay_sim_time(3)456# start the test itself, go through some modes and check if anything changes457previous_altitude = self.mav.recv_match(type='VFR_HUD', blocking=True).alt458self.change_mode('ALT_HOLD')459self.delay_sim_time(2)460self.change_mode('POSHOLD')461self.delay_sim_time(2)462self.change_mode('STABILIZE')463self.delay_sim_time(2)464self.change_mode(21)465self.delay_sim_time(2)466self.change_mode('ALT_HOLD')467self.delay_sim_time(2)468self.change_mode('STABILIZE')469self.delay_sim_time(2)470self.change_mode('ALT_HOLD')471self.delay_sim_time(2)472self.change_mode(21)473self.delay_sim_time(2)474self.change_mode('MANUAL')475self.disarm_vehicle()476final_altitude = self.mav.recv_match(type='VFR_HUD', blocking=True).alt477if abs(previous_altitude - final_altitude) > delta:478raise NotAchievedException(479"Changing modes affected depth with no throttle input!, started at {}, ended at {}"480.format(previous_altitude, final_altitude)481)482483def PositionHold(self):484"""Test POSHOLD mode"""485self.wait_ready_to_arm()486self.arm_vehicle()487# point North488self.reach_heading_manual(0)489self.change_mode('POSHOLD')490491# dive a little492self.set_rc(Joystick.Throttle, 1300)493self.delay_sim_time(3)494self.set_rc(Joystick.Throttle, 1500)495self.delay_sim_time(2)496497# Save starting point498msg = self.mav.recv_match(type='GLOBAL_POSITION_INT', blocking=True, timeout=5)499if msg is None:500raise NotAchievedException("Did not get GLOBAL_POSITION_INT")501start_pos = self.mav.location()502# Hold in perfect conditions503self.progress("Testing position hold in perfect conditions")504self.delay_sim_time(10)505distance_m = self.get_distance(start_pos, self.mav.location())506if distance_m > 1:507raise NotAchievedException("Position Hold was unable to keep position in calm waters within 1 meter after 10 seconds, drifted {} meters".format(distance_m)) # noqa508509# Hold in 1 m/s current510self.progress("Testing position hold in current")511self.set_parameter("SIM_WIND_SPD", 1)512self.set_parameter("SIM_WIND_T", 1)513self.delay_sim_time(10)514distance_m = self.get_distance(start_pos, self.mav.location())515if distance_m > 1:516raise NotAchievedException("Position Hold was unable to keep position in 1m/s current within 1 meter after 10 seconds, drifted {} meters".format(distance_m)) # noqa517518# Move forward slowly in 1 m/s current519start_pos = self.mav.location()520self.progress("Testing moving forward in position hold in 1m/s current")521self.set_rc(Joystick.Forward, 1600)522self.delay_sim_time(10)523distance_m = self.get_distance(start_pos, self.mav.location())524bearing = self.get_bearing(start_pos, self.mav.location())525if distance_m < 2 or (bearing > 30 and bearing < 330):526raise NotAchievedException("Position Hold was unable to move north 2 meters, moved {} at {} degrees instead".format(distance_m, bearing)) # noqa527self.disarm_vehicle()528529def MotorThrustHoverParameterIgnore(self):530"""Test if we are ignoring MOT_THST_HOVER parameter"""531532# Test default parameter value533mot_thst_hover_value = self.get_parameter("MOT_THST_HOVER")534if mot_thst_hover_value != 0.5:535raise NotAchievedException("Unexpected default MOT_THST_HOVER parameter value {}".format(mot_thst_hover_value))536537# Test if parameter is being ignored538for value in [0.25, 0.75]:539self.set_parameter("MOT_THST_HOVER", value)540self.AltitudeHold()541542def DiveManual(self):543'''Dive manual'''544self.wait_ready_to_arm()545self.arm_vehicle()546547self.set_rc(Joystick.Throttle, 1600)548self.set_rc(Joystick.Forward, 1600)549self.set_rc(Joystick.Lateral, 1550)550551self.wait_distance(50, accuracy=7, timeout=200)552self.set_rc(Joystick.Yaw, 1550)553554self.wait_heading(0)555self.set_rc(Joystick.Yaw, 1500)556557self.wait_distance(50, accuracy=7, timeout=100)558self.set_rc(Joystick.Yaw, 1550)559560self.wait_heading(0)561self.set_rc(Joystick.Yaw, 1500)562self.set_rc(Joystick.Forward, 1500)563self.set_rc(Joystick.Lateral, 1100)564565self.wait_distance(75, accuracy=7, timeout=100)566self.set_rc_default()567568self.disarm_vehicle()569self.progress("Manual dive OK")570571m = self.assert_receive_message('SCALED_PRESSURE3')572573# Note this temperature matches the output of the Atmospheric Model for Air currently574# And should be within 1 deg C of 40 degC575if (m.temperature < 3900) or (4100 < m.temperature):576raise NotAchievedException("Did not get correct TSYS01 temperature: Got %f" % m.temperature)577578def DiveMission(self):579'''Dive mission'''580filename = "sub_mission.txt"581self.progress("Executing mission %s" % filename)582self.load_mission(filename)583self.set_rc_default()584585self.arm_vehicle()586587self.change_mode('AUTO')588589self.wait_waypoint(1, 5, max_dist=5)590591self.disarm_vehicle()592593self.progress("Mission OK")594595def GripperMission(self):596'''Test gripper mission items'''597self.load_mission("sub-gripper-mission.txt")598self.wait_ready_to_arm()599self.arm_vehicle()600self.change_mode('AUTO')601self.wait_waypoint(1, 2, max_dist=5)602self.wait_statustext("Gripper Grabbed", timeout=60)603self.wait_waypoint(1, 4, max_dist=5)604self.wait_statustext("Gripper Released", timeout=60)605self.wait_waypoint(1, 6, max_dist=5)606self.disarm_vehicle()607608def SET_POSITION_TARGET_GLOBAL_INT(self):609'''Move vehicle using SET_POSITION_TARGET_GLOBAL_INT'''610self.change_mode('GUIDED')611self.wait_ready_to_arm()612self.arm_vehicle()613614startpos = self.mav.recv_match(type='GLOBAL_POSITION_INT',615blocking=True)616617lat = 5618lon = 5619alt = -10620621# send a position-control command622self.mav.mav.set_position_target_global_int_send(6230, # timestamp6241, # target system_id6251, # target component id626mavutil.mavlink.MAV_FRAME_GLOBAL_RELATIVE_ALT_INT,6270b1111111111111000, # mask specifying use-only-lat-lon-alt628lat, # lat629lon, # lon630alt, # alt6310, # vx6320, # vy6330, # vz6340, # afx6350, # afy6360, # afz6370, # yaw6380, # yawrate639)640641tstart = self.get_sim_time()642while True:643if self.get_sim_time_cached() - tstart > 200:644raise NotAchievedException("Did not move far enough")645pos = self.mav.recv_match(type='GLOBAL_POSITION_INT',646blocking=True)647delta = self.get_distance_int(startpos, pos)648self.progress("delta=%f (want >10)" % delta)649if delta > 10:650break651self.change_mode('MANUAL')652self.disarm_vehicle()653654def DoubleCircle(self):655'''Test entering circle twice'''656self.change_mode('CIRCLE')657self.wait_ready_to_arm()658self.arm_vehicle()659self.change_mode('STABILIZE')660self.change_mode('CIRCLE')661self.disarm_vehicle()662663def default_parameter_list(self):664ret = super(AutoTestSub, self).default_parameter_list()665ret["FS_GCS_ENABLE"] = 0 # FIXME666return ret667668def MAV_CMD_NAV_LOITER_UNLIM(self):669'''test handling of MAV_CMD_NAV_LOITER_UNLIM received via mavlink'''670for cmd in self.run_cmd, self.run_cmd_int:671self.change_mode('CIRCLE')672cmd(mavutil.mavlink.MAV_CMD_NAV_LOITER_UNLIM)673self.assert_mode('POSHOLD')674675def MAV_CMD_NAV_LAND(self):676'''test handling of MAV_CMD_NAV_LAND received via mavlink'''677for cmd in self.run_cmd, self.run_cmd_int:678self.change_mode('CIRCLE')679cmd(mavutil.mavlink.MAV_CMD_NAV_LAND)680self.assert_mode('SURFACE')681682def MAV_CMD_MISSION_START(self):683'''test handling of MAV_CMD_MISSION_START received via mavlink'''684self.upload_simple_relhome_mission([685(mavutil.mavlink.MAV_CMD_NAV_WAYPOINT, 2000, 0, 0),686(mavutil.mavlink.MAV_CMD_NAV_RETURN_TO_LAUNCH, 0, 0, 0),687])688689self.wait_ready_to_arm()690self.arm_vehicle()691for cmd in self.run_cmd, self.run_cmd_int:692self.change_mode('CIRCLE')693cmd(mavutil.mavlink.MAV_CMD_MISSION_START)694self.assert_mode('AUTO')695self.disarm_vehicle()696697def MAV_CMD_DO_CHANGE_SPEED(self):698'''ensure vehicle changes speeds when DO_CHANGE_SPEED received'''699items = [700(mavutil.mavlink.MAV_CMD_NAV_WAYPOINT, 0, 0, -3), # Dive so we have constant drag701(mavutil.mavlink.MAV_CMD_NAV_WAYPOINT, 2000, 0, -1),702(mavutil.mavlink.MAV_CMD_NAV_RETURN_TO_LAUNCH, 0, 0, 0),703]704self.upload_simple_relhome_mission(items)705self.wait_ready_to_arm()706self.arm_vehicle()707self.run_cmd(mavutil.mavlink.MAV_CMD_MISSION_START)708self.progress("SENT MISSION START")709self.wait_mode('AUTO')710self.wait_current_waypoint(2) # wait after we finish diving to 3m711for run_cmd in self.run_cmd, self.run_cmd_int:712for speed in [1, 1.5, 0.5]:713run_cmd(mavutil.mavlink.MAV_CMD_DO_CHANGE_SPEED, p2=speed)714self.wait_groundspeed(speed-0.2, speed+0.2, minimum_duration=2, timeout=60)715self.disarm_vehicle()716717def _MAV_CMD_CONDITION_YAW(self, run_cmd):718self.arm_vehicle()719self.change_mode('GUIDED')720for angle in 5, 30, 60, 10:721angular_rate = 10722direction = 1723relative_or_absolute = 0724run_cmd(725mavutil.mavlink.MAV_CMD_CONDITION_YAW,726p1=angle,727p2=angular_rate,728p3=direction,729p4=relative_or_absolute, # 1 for relative, 0 for absolute730)731self.wait_heading(angle, minimum_duration=2)732733self.start_subtest('Relative angle')734run_cmd(735mavutil.mavlink.MAV_CMD_CONDITION_YAW,736p1=0,737p2=10,738p3=1,739p4=0, # 1 for relative, 0 for absolute740)741self.wait_heading(0, minimum_duration=2)742run_cmd(743mavutil.mavlink.MAV_CMD_CONDITION_YAW,744p1=20,745p2=10,746p3=1,747p4=1, # 1 for relative, 0 for absolute748)749self.wait_heading(20, minimum_duration=2)750751self.disarm_vehicle()752753def MAV_CMD_CONDITION_YAW(self):754'''ensure vehicle yaws according to GCS command'''755self._MAV_CMD_CONDITION_YAW(self.run_cmd)756self._MAV_CMD_CONDITION_YAW(self.run_cmd_int)757758def MAV_CMD_DO_REPOSITION(self):759"""Move vehicle using MAV_CMD_DO_REPOSITION"""760self.wait_ready_to_arm()761self.arm_vehicle()762763# Dive so that rangefinder is in range, required for MAV_FRAME_GLOBAL_TERRAIN_ALT764start_altitude = -25765pwm = 1300 if self.get_altitude(relative=True) > start_altitude else 1700766self.set_rc(Joystick.Throttle, pwm)767self.wait_altitude(altitude_min=start_altitude-1, altitude_max=start_altitude, relative=False, timeout=120)768self.set_rc(Joystick.Throttle, 1500)769self.change_mode('GUIDED')770771loc = self.mav.location()772773# Reposition, alt relative to surface774loc = self.offset_location_ne(loc, 10, 10)775loc.alt = start_altitude776self.send_do_reposition(loc, frame=mavutil.mavlink.MAV_FRAME_GLOBAL_RELATIVE_ALT)777self.wait_location(loc, timeout=120)778779# Reposition, alt relative to seafloor780loc = self.offset_location_ne(loc, 10, 10)781loc.alt = -start_altitude782self.send_do_reposition(loc, frame=mavutil.mavlink.MAV_FRAME_GLOBAL_TERRAIN_ALT)783self.wait_location(loc, timeout=120)784785self.disarm_vehicle()786787def TerrainMission(self):788"""Mission using surface tracking"""789790if self.get_parameter('RNGFND1_MAX_CM') != 3000.0:791raise PreconditionFailedException("RNGFND1_MAX_CM is not %g" % 3000.0)792793filename = "terrain_mission.txt"794self.progress("Executing mission %s" % filename)795self.load_mission(filename)796self.set_rc_default()797self.arm_vehicle()798self.change_mode('AUTO')799self.wait_waypoint(1, 4, max_dist=5)800self.delay_sim_time(3)801802# Expect sub to hover at final altitude803self.assert_altitude(-36.0)804805self.disarm_vehicle()806self.progress("Mission OK")807808def wait_ekf_happy_const_pos(self, timeout=45):809# All of these must be set for arming to happen in constant position mode:810required_value = (mavutil.mavlink.EKF_ATTITUDE |811mavutil.mavlink.EKF_VELOCITY_HORIZ |812mavutil.mavlink.EKF_VELOCITY_VERT |813mavutil.mavlink.EKF_POS_VERT_ABS |814mavutil.mavlink.EKF_CONST_POS_MODE)815816# None of these bits must be set for arming to happen:817error_bits = mavutil.mavlink.EKF_UNINITIALIZED818819self.wait_ekf_flags(required_value, error_bits, timeout=timeout)820821def wait_ready_to_arm_const_pos(self, timeout=120):822self.progress("Waiting for ready to arm (constant position mode)")823start = self.get_sim_time()824self.wait_ekf_happy_const_pos(timeout=timeout)825armable_time = self.get_sim_time() - start826self.progress("Took %u seconds to become armable" % armable_time)827self.total_waiting_to_arm_time += armable_time828self.waiting_to_arm_count += 1829830def collected_msgs(self, msg_type):831c = self.context_get()832if msg_type not in c.collections:833raise NotAchievedException("Not collecting (%s)" % str(msg_type))834return c.collections[msg_type]835836def SetGlobalOrigin(self):837"""Test SET_GPS_GLOBAL_ORIGIN mav msg"""838self.context_push()839self.set_parameters({840'GPS1_TYPE': 0, # Disable the GPS841'EK3_SRC1_POSXY': 0, # Make sure EK3_SRC parameters do not refer to a GPS842})843self.reboot_sitl()844845# Wait for the EKF to be happy in constant position mode846self.wait_ready_to_arm_const_pos()847848if self.current_onboard_log_contains_message('ORGN'):849raise NotAchievedException("Found unexpected ORGN message")850851self.context_collect('GPS_GLOBAL_ORIGIN')852853# This should set the EKF origin, write an ORGN msg to df and a GPS_GLOBAL_ORIGIN msg to MAVLink854self.mav.mav.set_gps_global_origin_send(1, int(47.607584 * 1e7), int(-122.343911 * 1e7), 0)855self.delay_sim_time(1)856857if not self.current_onboard_log_contains_message('ORGN'):858raise NotAchievedException("Did not find expected ORGN message")859860num_mavlink_origin_msgs = len(self.collected_msgs('GPS_GLOBAL_ORIGIN'))861if num_mavlink_origin_msgs != 1:862raise NotAchievedException("Expected 1 GPS_GLOBAL_ORIGIN message, found %d" % num_mavlink_origin_msgs)863864self.context_pop()865866# restart GPS driver867self.reboot_sitl()868869def BackupOrigin(self):870"""Test ORIGIN_LAT and ORIGIN_LON parameters"""871872self.context_push()873self.set_parameters({874'GPS1_TYPE': 0, # Disable GPS875'EK3_SRC1_POSXY': 0, # Make sure EK3_SRC parameters do not refer to GPS876'EK3_SRC1_VELXY': 0, # Make sure EK3_SRC parameters do not refer to GPS877'ORIGIN_LAT': 47.607584,878'ORIGIN_LON': -122.343911,879})880self.reboot_sitl()881self.context_collect('STATUSTEXT')882883# Wait for the EKF to be happy in constant position mode884self.wait_ready_to_arm_const_pos()885886if self.current_onboard_log_contains_message('ORGN'):887raise NotAchievedException("Found unexpected ORGN message")888889# This should set the origin and write a record to ORGN890self.arm_vehicle()891892self.wait_statustext('Using backup location', check_context=True)893894if not self.current_onboard_log_contains_message('ORGN'):895raise NotAchievedException("Did not find expected ORGN message")896897self.disarm_vehicle()898self.context_pop()899900def assert_mag_fusion_selection(self, expect_sel):901"""Get the most recent XKFS message and check the MAG_FUSION value"""902self.progress("Expect mag fusion selection %d" % expect_sel)903mlog = self.dfreader_for_current_onboard_log()904found_sel = MagFuseSel.NOT_FUSING905while True:906m = mlog.recv_match(type='XKFS')907if m is None:908break909found_sel = m.MAG_FUSION910if found_sel != expect_sel:911raise NotAchievedException("Expected mag fusion selection %d, found %d" % (expect_sel, found_sel))912913def FuseMag(self):914"""Test EK3_MAG_CAL values"""915916# WHEN_FLYING: switch to FUSE_MAG after sub is armed for 5 seconds; switch to FUSE_YAW on disarm917self.set_parameters({'EK3_MAG_CAL': MagCal.WHEN_FLYING})918self.reboot_sitl()919self.wait_ready_to_arm()920self.assert_mag_fusion_selection(MagFuseSel.FUSE_YAW)921self.arm_vehicle()922self.delay_sim_time(10)923self.assert_mag_fusion_selection(MagFuseSel.FUSE_MAG)924self.disarm_vehicle()925self.delay_sim_time(1)926self.assert_mag_fusion_selection(MagFuseSel.FUSE_YAW)927928# AFTER_FIRST_CLIMB: switch to FUSE_MAG after sub is armed and descends 0.5m; switch to FUSE_YAW on disarm929self.set_parameters({'EK3_MAG_CAL': MagCal.AFTER_FIRST_CLIMB})930self.reboot_sitl()931self.wait_ready_to_arm()932self.assert_mag_fusion_selection(MagFuseSel.FUSE_YAW)933altitude = self.get_altitude(relative=True)934self.arm_vehicle()935self.set_rc(Joystick.Throttle, 1300)936self.wait_altitude(altitude_min=altitude-4, altitude_max=altitude-3, relative=False, timeout=60)937self.set_rc(Joystick.Throttle, 1500)938self.assert_mag_fusion_selection(MagFuseSel.FUSE_MAG)939self.disarm_vehicle()940self.delay_sim_time(1)941self.assert_mag_fusion_selection(MagFuseSel.FUSE_YAW)942943# ALWAYS944self.set_parameters({'EK3_MAG_CAL': MagCal.ALWAYS})945self.reboot_sitl()946self.wait_ready_to_arm()947self.assert_mag_fusion_selection(MagFuseSel.FUSE_MAG)948949def INA3221(self):950'''test INA3221 driver'''951self.set_parameters({952"BATT2_MONITOR": 30,953"BATT3_MONITOR": 30,954"BATT4_MONITOR": 30,955})956self.reboot_sitl()957self.set_parameters({958"BATT2_I2C_ADDR": 0x42, # address defined in libraries/SITL/SIM_I2C.cpp959"BATT2_I2C_BUS": 1,960"BATT2_CHANNEL": 1,961962"BATT3_I2C_ADDR": 0x42,963"BATT3_I2C_BUS": 1,964"BATT3_CHANNEL": 2,965966"BATT4_I2C_ADDR": 0x42,967"BATT4_I2C_BUS": 1,968"BATT4_CHANNEL": 3,969})970self.reboot_sitl()971972seen_1 = False973seen_3 = False974tstart = self.get_sim_time()975while not (seen_1 and seen_3):976m = self.assert_receive_message('BATTERY_STATUS')977if self.get_sim_time() - tstart > 10:978# expected to take under 1 simulated second, but don't hang if979# e.g. the driver gets stuck980raise NotAchievedException("INA3221 status timeout")981if m.id == 1:982self.assert_message_field_values(m, {983# values close to chip limits984"voltages[0]": int(25 * 1000), # millivolts985"current_battery": int(160 * 100), # centi-amps986}, epsilon=10) # allow rounding987seen_1 = True988# id 2 is the first simulated battery current/voltage989if m.id == 3:990self.assert_message_field_values(m, {991# values different from above to test channel switching992"voltages[0]": int(3.14159 * 1000), # millivolts993"current_battery": int(2.71828 * 100), # centi-amps994}, epsilon=10) # allow rounding995seen_3 = True996997def tests(self):998'''return list of all tests'''999ret = super(AutoTestSub, self).tests()10001001ret.extend([1002self.DiveManual,1003self.AltitudeHold,1004self.Surftrak,1005self.SimTerrainSurftrak,1006self.SimTerrainMission,1007self.RngfndQuality,1008self.PositionHold,1009self.ModeChanges,1010self.DiveMission,1011self.GripperMission,1012self.DoubleCircle,1013self.MotorThrustHoverParameterIgnore,1014self.SET_POSITION_TARGET_GLOBAL_INT,1015self.TestLogDownloadMAVProxy,1016self.TestLogDownloadMAVProxyNetwork,1017self.TestLogDownloadLogRestart,1018self.MAV_CMD_NAV_LOITER_UNLIM,1019self.MAV_CMD_NAV_LAND,1020self.MAV_CMD_MISSION_START,1021self.MAV_CMD_DO_CHANGE_SPEED,1022self.MAV_CMD_CONDITION_YAW,1023self.MAV_CMD_DO_REPOSITION,1024self.TerrainMission,1025self.SetGlobalOrigin,1026self.BackupOrigin,1027self.FuseMag,1028self.INA3221,1029])10301031return ret103210331034