Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Ardupilot
GitHub Repository: Ardupilot/ardupilot
Path: blob/master/Tools/autotest/ardusub.py
9732 views
1
'''
2
Dive ArduSub in SITL
3
4
Depth of water is 50m, the ground is flat
5
Parameters are in-code defaults plus default_params/sub.parm
6
7
AP_FLAKE8_CLEAN
8
'''
9
10
import os
11
12
from pymavlink import mavutil, mavextra
13
14
import vehicle_test_suite
15
from vehicle_test_suite import NotAchievedException
16
from math import degrees
17
18
# get location of scripts
19
testdir = os.path.dirname(os.path.realpath(__file__))
20
21
SITL_START_LOCATION = mavutil.location(33.810313, -118.393867, 0, 185)
22
23
24
class Joystick():
25
Pitch = 1
26
Roll = 2
27
Throttle = 3
28
Yaw = 4
29
Forward = 5
30
Lateral = 6
31
32
33
# Values for EK3_MAG_CAL
34
class MagCal():
35
WHEN_FLYING = 0
36
WHEN_MANOEUVRING = 1
37
NEVER = 2
38
AFTER_FIRST_CLIMB = 3
39
ALWAYS = 4
40
41
42
# Values for XKFS.MAG_FUSION
43
class MagFuseSel():
44
NOT_FUSING = 0
45
FUSE_YAW = 1
46
FUSE_MAG = 2
47
48
49
class AutoTestSub(vehicle_test_suite.TestSuite):
50
@staticmethod
51
def get_not_armable_mode_list():
52
return []
53
54
@staticmethod
55
def get_not_disarmed_settable_modes_list():
56
return []
57
58
@staticmethod
59
def get_no_position_not_settable_modes_list():
60
return ["AUTO", "GUIDED", "CIRCLE", "POSHOLD"]
61
62
@staticmethod
63
def get_position_armable_modes_list():
64
return []
65
66
@staticmethod
67
def get_normal_armable_modes_list():
68
return ["ACRO", "ALT_HOLD", "MANUAL", "STABILIZE", "SURFACE"]
69
70
def log_name(self):
71
return "ArduSub"
72
73
def default_speedup(self):
74
'''Sub seems to be race-free'''
75
return 100
76
77
def test_filepath(self):
78
return os.path.realpath(__file__)
79
80
def set_current_test_name(self, name):
81
self.current_test_name_directory = "ArduSub_Tests/" + name + "/"
82
83
def default_mode(self):
84
return 'MANUAL'
85
86
def sitl_start_location(self):
87
return SITL_START_LOCATION
88
89
def default_frame(self):
90
return 'vectored'
91
92
def is_sub(self):
93
return True
94
95
def watch_altitude_maintained(self, delta=0.3, timeout=5.0):
96
"""Watch and wait for the actual altitude to be maintained
97
98
Keyword Arguments:
99
delta {float} -- Maximum altitude range to be allowed from actual point (default: {0.5})
100
timeout {float} -- Timeout time in simulation seconds (default: {5.0})
101
102
Raises:
103
NotAchievedException: Exception when altitude fails to hold inside the time and
104
altitude range
105
"""
106
tstart = self.get_sim_time_cached()
107
previous_altitude = self.assert_receive_message('VFR_HUD').alt
108
self.progress('Altitude to be watched: %f' % (previous_altitude))
109
while True:
110
m = self.assert_receive_message('VFR_HUD')
111
if self.get_sim_time_cached() - tstart > timeout:
112
self.progress('Altitude hold done: %f' % (previous_altitude))
113
return
114
if abs(m.alt - previous_altitude) > delta:
115
raise NotAchievedException(
116
"Altitude not maintained: want %.2f (+/- %.2f) got=%.2f" %
117
(previous_altitude, delta, m.alt))
118
119
def AltitudeHold(self):
120
"""Test ALT_HOLD mode"""
121
self.wait_ready_to_arm()
122
self.arm_vehicle()
123
self.change_mode('ALT_HOLD')
124
125
msg = self.assert_receive_message('GLOBAL_POSITION_INT', timeout=5)
126
pwm = 1300
127
if msg.relative_alt/1000.0 < -6.0:
128
# need to go up, not down!
129
pwm = 1700
130
self.set_rc(Joystick.Throttle, pwm)
131
self.wait_altitude(altitude_min=-6, altitude_max=-5)
132
self.set_rc(Joystick.Throttle, 1500)
133
134
# let the vehicle settle (momentum / stopping point shenanigans....)
135
self.delay_sim_time(1)
136
137
self.watch_altitude_maintained()
138
139
self.set_rc(Joystick.Throttle, 1000)
140
self.wait_altitude(altitude_min=-20, altitude_max=-19)
141
self.set_rc(Joystick.Throttle, 1500)
142
143
# let the vehicle settle (momentum / stopping point shenanigans....)
144
self.delay_sim_time(1)
145
146
self.watch_altitude_maintained()
147
148
self.set_rc(Joystick.Throttle, 1900)
149
self.wait_altitude(altitude_min=-14, altitude_max=-13)
150
self.set_rc(Joystick.Throttle, 1500)
151
152
# let the vehicle settle (momentum / stopping point shenanigans....)
153
self.delay_sim_time(1)
154
155
self.watch_altitude_maintained()
156
157
self.set_rc(Joystick.Throttle, 1900)
158
self.wait_altitude(altitude_min=-5, altitude_max=-4)
159
self.set_rc(Joystick.Throttle, 1500)
160
161
# let the vehicle settle (momentum / stopping point shenanigans....)
162
self.delay_sim_time(1)
163
self.watch_altitude_maintained()
164
165
# Make sure the code can handle buoyancy changes
166
self.set_parameter("SIM_BUOYANCY", 10)
167
self.watch_altitude_maintained()
168
self.set_parameter("SIM_BUOYANCY", -10)
169
self.watch_altitude_maintained()
170
171
# Make sure that the ROV will dive with a small input down even if there is a 10N buoyancy force upwards
172
self.set_parameter("SIM_BUOYANCY", 10)
173
self.set_rc(Joystick.Throttle, 1350)
174
self.wait_altitude(altitude_min=-6, altitude_max=-5.5)
175
176
self.set_rc(Joystick.Throttle, 1500)
177
self.watch_altitude_maintained()
178
self.disarm_vehicle()
179
180
def RngfndQuality(self):
181
"""Check lua Range Finder quality information flow"""
182
self.context_collect('STATUSTEXT')
183
184
self.set_parameters({
185
"SCR_ENABLE": 1,
186
"RNGFND1_TYPE": 36,
187
"RNGFND1_ORIENT": 25,
188
"RNGFND1_MIN": 0.10,
189
"RNGFND1_MAX": 50.00,
190
})
191
192
self.install_example_script_context("rangefinder_quality_test.lua")
193
194
# These string must match those sent by the lua test script.
195
complete_str = "#complete#"
196
failure_str = "!!failure!!"
197
198
self.reboot_sitl()
199
200
self.wait_statustext(complete_str, timeout=20, check_context=True)
201
found_failure = self.statustext_in_collections(failure_str)
202
203
if found_failure is not None:
204
raise NotAchievedException("RngfndQuality test failed: " + found_failure.text)
205
206
def watch_distance_maintained(self, delta=0.3, timeout=5.0):
207
"""Watch and wait for the rangefinder reading to be maintained"""
208
tstart = self.get_sim_time_cached()
209
self.context_push()
210
self.context_set_message_rate_hz('RANGEFINDER', self.sitl_streamrate())
211
previous_distance = self.assert_receive_message('RANGEFINDER').distance
212
previous_distance_ds = self.assert_receive_message('DISTANCE_SENSOR').current_distance * 0.01 # cm -> m
213
self.progress('Distance to be watched: %.2f' % previous_distance)
214
while True:
215
if self.get_sim_time_cached() - tstart > timeout:
216
self.progress('Distance hold done: %f' % previous_distance)
217
self.context_pop()
218
return
219
m = self.assert_receive_message('RANGEFINDER')
220
if abs(m.distance - previous_distance) > delta:
221
raise NotAchievedException(
222
"Distance not maintained: want %.2f (+/- %.2f) got=%.2f" %
223
(previous_distance, delta, m.distance))
224
m = self.assert_receive_message('DISTANCE_SENSOR')
225
if abs(m.current_distance*0.01 - previous_distance_ds) > delta:
226
raise NotAchievedException(
227
"Distance not maintained: want %.2f (+/- %.2f) got=%.2f" %
228
(previous_distance, delta, m.distance))
229
230
def GCSFailsafe(self):
231
'''Test GCSFailsafe'''
232
self.wait_ready_to_arm()
233
self.arm_vehicle()
234
self.set_parameter("MAV_GCS_SYSID", self.mav.source_system)
235
self.context_push()
236
self.setGCSfailsafe(4)
237
self.set_heartbeat_rate(0)
238
self.wait_mode("SURFACE")
239
self.set_heartbeat_rate(self.speedup)
240
self.wait_statustext("GCS Failsafe Cleared", timeout=60)
241
self.progress("GSC Failsafe OK")
242
self.disarm_vehicle()
243
self.context_pop()
244
245
# Tests actions and logic behind the radio failsafe
246
def ThrottleFailsafe(self):
247
'''Test RC and RC Failsafe'''
248
# disable GCS and enable RC
249
self.context_push()
250
self.set_parameter('FS_GCS_ENABLE', 0)
251
self.set_parameter('RC_PROTOCOLS', 1)
252
self.set_heartbeat_rate(0)
253
# Trigger an RC failure with the failsafe disabled. Verify no action taken.
254
self.start_subtest("Radio failsafe disabled test: FS_THR_ENABLE=0 should take no failsafe action")
255
self.set_parameter('FS_THR_ENABLE', 0)
256
self.set_parameter("SIM_RC_FAIL", 1)
257
self.wait_ready_to_arm()
258
# Switch RC failsafe action to SURFACE, should take action since we are in RC failsafe
259
self.set_parameter('FS_THR_ENABLE', 2)
260
self.wait_mode("SURFACE")
261
self.context_pop()
262
self.progress("Completed Radio failsafe disabled test")
263
264
def Surftrak(self):
265
"""Test SURFTRAK mode"""
266
267
self.assert_parameter_value('RNGFND1_MAX', 30)
268
269
# Something closer to Bar30 noise
270
self.context_push()
271
self.set_parameter("SIM_BARO_RND", 0.01)
272
273
self.wait_ready_to_arm()
274
self.arm_vehicle()
275
self.change_mode('MANUAL')
276
277
# Dive to -5m, outside of rangefinder range, will act like ALT_HOLD
278
pwm = 1300 if self.get_altitude(relative=True) > -6 else 1700
279
self.set_rc(Joystick.Throttle, pwm)
280
self.wait_altitude(altitude_min=-6, altitude_max=-5, relative=False, timeout=60)
281
self.set_rc(Joystick.Throttle, 1500)
282
self.delay_sim_time(1)
283
self.context_collect('STATUSTEXT')
284
self.change_mode(21)
285
self.wait_statustext('waiting for a rangefinder reading', check_context=True)
286
self.context_clear_collection('STATUSTEXT')
287
self.watch_altitude_maintained()
288
289
# Move into range, should set a rangefinder target and maintain it
290
self.set_rc(Joystick.Throttle, 1300)
291
self.wait_altitude(altitude_min=-26, altitude_max=-25, relative=False, timeout=60)
292
self.set_rc(Joystick.Throttle, 1500)
293
self.delay_sim_time(4)
294
self.wait_statustext('rangefinder target is', check_context=True)
295
self.context_clear_collection('STATUSTEXT')
296
self.watch_distance_maintained()
297
298
# Move a few meters, should apply a delta and maintain the new rangefinder target
299
self.set_rc(Joystick.Throttle, 1300)
300
self.wait_altitude(altitude_min=-31, altitude_max=-30, relative=False, timeout=60)
301
self.set_rc(Joystick.Throttle, 1500)
302
self.delay_sim_time(4)
303
self.wait_statustext('rangefinder target is', check_context=True)
304
self.watch_distance_maintained()
305
306
# Disarm, allowing the vehicle to drift up
307
self.disarm_vehicle()
308
self.delay_sim_time(5)
309
310
# Re-arm. The vehicle should get a new rangefinder target and maintain distance
311
self.arm_vehicle()
312
self.watch_distance_maintained()
313
314
self.disarm_vehicle()
315
self.context_pop()
316
317
def prepare_synthetic_seafloor_test(self, sea_floor_depth, rf_target):
318
self.set_parameters({
319
"SCR_ENABLE": 1,
320
"RNGFND1_TYPE": 36,
321
"RNGFND1_ORIENT": 25,
322
"RNGFND1_MIN": 0.10,
323
"RNGFND1_MAX": 30.00,
324
"SCR_USER1": 2, # Configuration bundle
325
"SCR_USER2": sea_floor_depth, # Depth in meters
326
"SCR_USER3": 101, # Output log records
327
"SCR_USER4": rf_target, # Rangefinder target in meters
328
})
329
330
self.install_example_script_context("sub_test_synthetic_seafloor.lua")
331
332
# Reboot to enable scripting.
333
self.reboot_sitl()
334
self.set_rc_default()
335
self.wait_ready_to_arm()
336
337
def watch_true_distance_maintained(self, match_distance, delta=0.3, timeout=5.0, final_waypoint=0):
338
"""Watch and wait for the rangefinder reading to be maintained"""
339
340
def get_true_distance():
341
"""Return the True distance from the simulated range finder"""
342
m_true = self.mav.recv_match(type='STATUSTEXT', blocking=True, timeout=3.0)
343
if m_true is None:
344
return m_true
345
idx_tr = m_true.text.find('#TR#')
346
if idx_tr < 0:
347
return None
348
return float(m_true.text[(idx_tr+4):(idx_tr+12)])
349
350
tstart = self.get_sim_time_cached()
351
self.progress('Distance to be watched: %.2f (+/- %.2f)' % (match_distance, delta))
352
max_delta = 0.0
353
354
while True:
355
timed_out = self.get_sim_time_cached() - tstart > timeout
356
# If final_waypoint>0 then timeout is failure, otherwise success
357
if timed_out and final_waypoint > 0:
358
raise NotAchievedException(
359
"Mission not complete: want waypoint %i, only made it to waypoint %i" %
360
(final_waypoint, self.mav.waypoint_current()))
361
if timed_out:
362
self.progress('Distance hold done. Max delta:%.2fm' % max_delta)
363
return
364
365
true_distance = get_true_distance()
366
if true_distance is None:
367
continue
368
match_delta = abs(true_distance - match_distance)
369
if match_delta > max_delta:
370
max_delta = match_delta
371
if match_delta > delta:
372
raise NotAchievedException(
373
"Distance not maintained: want %.2f (+/- %.2f) got=%.2f (%.2f)" %
374
(match_distance, delta, true_distance, match_delta))
375
if final_waypoint > 0:
376
if self.mav.waypoint_current() >= final_waypoint:
377
self.progress('Distance hold during mission done. Max delta:%.2fm' % max_delta)
378
return
379
380
def SimTerrainSurftrak(self):
381
"""Move at a constant height above synthetic sea floor"""
382
383
sea_floor_depth = 50 # Depth of sea floor at location of test
384
match_distance = 15 # Desired sub distance from sea floor
385
start_altitude = -sea_floor_depth+match_distance
386
end_altitude = start_altitude - 10
387
validation_delta = 1.5 # Largest allowed distance between sub height and desired height
388
389
self.context_push()
390
self.prepare_synthetic_seafloor_test(sea_floor_depth, match_distance)
391
self.change_mode('MANUAL')
392
self.arm_vehicle()
393
394
# Dive to match_distance off the bottom in preparation for the mission
395
pwm = 1300 if self.get_altitude(relative=True) > start_altitude else 1700
396
self.set_rc(Joystick.Throttle, pwm)
397
self.wait_altitude(altitude_min=start_altitude-1, altitude_max=start_altitude, relative=False, timeout=120)
398
self.set_rc(Joystick.Throttle, 1500)
399
self.delay_sim_time(1)
400
401
# Turn on surftrak and move around
402
self.change_mode(21)
403
404
# Go south over the ridge.
405
self.reach_heading_manual(180)
406
self.set_rc(Joystick.Forward, 1650)
407
self.watch_true_distance_maintained(match_distance, delta=validation_delta, timeout=60)
408
self.set_rc(Joystick.Forward, 1500)
409
410
# Shift west a bit
411
self.reach_heading_manual(270)
412
self.set_rc(Joystick.Forward, 1650)
413
self.watch_true_distance_maintained(match_distance, delta=validation_delta, timeout=5)
414
self.set_rc(Joystick.Forward, 1500)
415
416
# Go south over the plateau
417
self.reach_heading_manual(180)
418
self.set_rc(Joystick.Forward, 1650)
419
self.watch_true_distance_maintained(match_distance, delta=validation_delta, timeout=60)
420
421
# The mission ends at end_altitude. Do a check to ensure that the sub is at this altitude
422
self.wait_altitude(altitude_min=end_altitude-validation_delta/2, altitude_max=end_altitude+validation_delta/2,
423
relative=False, timeout=1)
424
425
self.set_rc(Joystick.Forward, 1500)
426
427
self.disarm_vehicle()
428
self.context_pop()
429
self.reboot_sitl() # e.g. revert rangefinder configuration
430
431
def SimTerrainMission(self):
432
"""Mission at a constant height above synthetic sea floor"""
433
434
sea_floor_depth = 50 # Depth of sea floor at location of test
435
match_distance = 15 # Desired sub distance from sea floor
436
start_altitude = -sea_floor_depth+match_distance
437
end_altitude = start_altitude - 10
438
validation_delta = 1.5 # Largest allowed distance between sub height and desired height
439
440
self.context_push()
441
self.prepare_synthetic_seafloor_test(sea_floor_depth, match_distance)
442
443
# The synthetic seafloor has an east-west ridge south of the sub.
444
# The mission contained in terrain_mission.txt instructs the sub
445
# to remain at 15m above the seafloor and travel south over the
446
# ridge. Then the sub moves west and travels north over the ridge.
447
filename = "terrain_mission.txt"
448
self.load_mission(filename)
449
450
self.change_mode('MANUAL')
451
self.arm_vehicle()
452
453
# Dive to match_distance off the bottom in preparation for the mission
454
pwm = 1300 if self.get_altitude(relative=True) > start_altitude else 1700
455
self.set_rc(Joystick.Throttle, pwm)
456
self.wait_altitude(altitude_min=start_altitude-1, altitude_max=start_altitude, relative=False, timeout=120)
457
self.set_rc(Joystick.Throttle, 1500)
458
self.delay_sim_time(1)
459
460
self.change_mode('AUTO')
461
self.watch_true_distance_maintained(match_distance, delta=validation_delta, timeout=500.0, final_waypoint=4)
462
463
# The mission ends at end_altitude. Do a check to ensure that the sub is at this altitude.
464
self.wait_altitude(altitude_min=end_altitude-validation_delta/2, altitude_max=end_altitude+validation_delta/2,
465
relative=False, timeout=1)
466
467
self.disarm_vehicle()
468
self.context_pop()
469
self.reboot_sitl() # e.g. revert rangefinder configuration
470
471
def ModeChanges(self, delta=0.2):
472
"""Check if alternating between ALTHOLD, STABILIZE, POSHOLD and SURFTRAK (mode 21) affects altitude"""
473
self.wait_ready_to_arm()
474
self.arm_vehicle()
475
# zero buoyancy and no baro noise
476
self.set_parameter("SIM_BUOYANCY", 0)
477
self.set_parameter("SIM_BARO_RND", 0)
478
# dive a bit to make sure we are not surfaced
479
self.change_mode('STABILIZE')
480
self.set_rc(Joystick.Throttle, 1350)
481
self.delay_sim_time(10)
482
self.set_rc(Joystick.Throttle, 1500)
483
self.delay_sim_time(3)
484
# start the test itself, go through some modes and check if anything changes
485
previous_altitude = self.assert_receive_message('VFR_HUD').alt
486
self.change_mode('ALT_HOLD')
487
self.delay_sim_time(2)
488
self.change_mode('POSHOLD')
489
self.delay_sim_time(2)
490
self.change_mode('STABILIZE')
491
self.delay_sim_time(2)
492
self.change_mode(21)
493
self.delay_sim_time(2)
494
self.change_mode('ALT_HOLD')
495
self.delay_sim_time(2)
496
self.change_mode('STABILIZE')
497
self.delay_sim_time(2)
498
self.change_mode('ALT_HOLD')
499
self.delay_sim_time(2)
500
self.change_mode(21)
501
self.delay_sim_time(2)
502
self.change_mode('MANUAL')
503
self.disarm_vehicle()
504
final_altitude = self.assert_receive_message('VFR_HUD').alt
505
if abs(previous_altitude - final_altitude) > delta:
506
raise NotAchievedException(
507
"Changing modes affected depth with no throttle input!, started at {}, ended at {}"
508
.format(previous_altitude, final_altitude)
509
)
510
511
def PositionHold(self):
512
"""Test POSHOLD mode"""
513
self.wait_ready_to_arm()
514
self.arm_vehicle()
515
# point North
516
self.reach_heading_manual(0)
517
self.change_mode('POSHOLD')
518
519
# dive a little
520
self.set_rc(Joystick.Throttle, 1300)
521
self.delay_sim_time(3)
522
self.set_rc(Joystick.Throttle, 1500)
523
self.delay_sim_time(2)
524
525
# Save starting point
526
self.assert_receive_message('GLOBAL_POSITION_INT', timeout=5)
527
start_pos = self.mav.location()
528
# Hold in perfect conditions
529
self.progress("Testing position hold in perfect conditions")
530
self.delay_sim_time(10)
531
distance_m = self.get_distance(start_pos, self.mav.location())
532
if distance_m > 1:
533
raise NotAchievedException("Position Hold was unable to keep position in calm waters within 1 meter after 10 seconds, drifted {} meters".format(distance_m)) # noqa
534
535
# Hold in 1 m/s current
536
self.progress("Testing position hold in current")
537
self.set_parameter("SIM_WIND_SPD", 1)
538
self.set_parameter("SIM_WIND_T", 1)
539
self.delay_sim_time(10)
540
distance_m = self.get_distance(start_pos, self.mav.location())
541
if distance_m > 1:
542
raise NotAchievedException("Position Hold was unable to keep position in 1m/s current within 1 meter after 10 seconds, drifted {} meters".format(distance_m)) # noqa
543
544
# Move forward slowly in 1 m/s current
545
start_pos = self.mav.location()
546
self.progress("Testing moving forward in position hold in 1m/s current")
547
self.set_rc(Joystick.Forward, 1600)
548
self.delay_sim_time(10)
549
distance_m = self.get_distance(start_pos, self.mav.location())
550
bearing = self.get_bearing(start_pos, self.mav.location())
551
if distance_m < 2 or (bearing > 30 and bearing < 330):
552
raise NotAchievedException("Position Hold was unable to move north 2 meters, moved {} at {} degrees instead".format(distance_m, bearing)) # noqa
553
self.disarm_vehicle()
554
555
def MotorThrustHoverParameterIgnore(self):
556
"""Test if we are ignoring MOT_THST_HOVER parameter"""
557
558
# Test default parameter value
559
mot_thst_hover_value = self.get_parameter("MOT_THST_HOVER")
560
if mot_thst_hover_value != 0.5:
561
raise NotAchievedException("Unexpected default MOT_THST_HOVER parameter value {}".format(mot_thst_hover_value))
562
563
# Test if parameter is being ignored
564
for value in [0.25, 0.75]:
565
self.set_parameter("MOT_THST_HOVER", value)
566
self.AltitudeHold()
567
568
def DiveManual(self):
569
'''Dive manual'''
570
self.wait_ready_to_arm()
571
self.arm_vehicle()
572
573
self.set_rc(Joystick.Throttle, 1600)
574
self.set_rc(Joystick.Forward, 1600)
575
self.set_rc(Joystick.Lateral, 1550)
576
577
self.wait_distance(50, accuracy=7, timeout=200)
578
self.set_rc(Joystick.Yaw, 1550)
579
580
self.wait_heading(0)
581
self.set_rc(Joystick.Yaw, 1500)
582
583
self.wait_distance(50, accuracy=7, timeout=100)
584
self.set_rc(Joystick.Yaw, 1550)
585
586
self.wait_heading(0)
587
self.set_rc(Joystick.Yaw, 1500)
588
self.set_rc(Joystick.Forward, 1500)
589
self.set_rc(Joystick.Lateral, 1100)
590
591
self.wait_distance(75, accuracy=7, timeout=100)
592
self.set_rc_default()
593
594
self.disarm_vehicle()
595
self.progress("Manual dive OK")
596
597
m = self.assert_receive_message('SCALED_PRESSURE3')
598
599
# Note this temperature matches the output of the Atmospheric Model for Air currently
600
# And should be within 1 deg C of 40 degC
601
if (m.temperature < 3900) or (4100 < m.temperature):
602
raise NotAchievedException("Did not get correct TSYS01 temperature: Got %f" % m.temperature)
603
604
def DiveMission(self):
605
'''Dive mission'''
606
filename = "sub_mission.txt"
607
self.progress("Executing mission %s" % filename)
608
self.load_mission(filename)
609
self.set_rc_default()
610
611
self.arm_vehicle()
612
613
self.change_mode('AUTO')
614
615
self.wait_waypoint(1, 5, max_dist=5)
616
617
self.disarm_vehicle()
618
619
self.progress("Mission OK")
620
621
def GripperMission(self):
622
'''Test gripper mission items'''
623
self.load_mission("sub-gripper-mission.txt")
624
self.wait_ready_to_arm()
625
self.arm_vehicle()
626
self.change_mode('AUTO')
627
self.wait_waypoint(1, 2, max_dist=5)
628
self.wait_statustext("Gripper Grabbed", timeout=60)
629
self.wait_waypoint(1, 4, max_dist=5)
630
self.wait_statustext("Gripper Released", timeout=60)
631
self.wait_waypoint(1, 6, max_dist=5)
632
self.disarm_vehicle()
633
634
def SET_POSITION_TARGET_GLOBAL_INT(self):
635
'''Move vehicle using SET_POSITION_TARGET_GLOBAL_INT'''
636
self.change_mode('GUIDED')
637
self.wait_ready_to_arm()
638
self.arm_vehicle()
639
640
startpos = self.assert_receive_message('GLOBAL_POSITION_INT')
641
642
lat = 5
643
lon = 5
644
alt = -10
645
646
# send a position-control command
647
self.mav.mav.set_position_target_global_int_send(
648
0, # timestamp
649
1, # target system_id
650
1, # target component id
651
mavutil.mavlink.MAV_FRAME_GLOBAL_RELATIVE_ALT_INT,
652
0b1111111111111000, # mask specifying use-only-lat-lon-alt
653
lat, # lat
654
lon, # lon
655
alt, # alt
656
0, # vx
657
0, # vy
658
0, # vz
659
0, # afx
660
0, # afy
661
0, # afz
662
0, # yaw
663
0, # yawrate
664
)
665
666
tstart = self.get_sim_time()
667
while True:
668
if self.get_sim_time_cached() - tstart > 200:
669
raise NotAchievedException("Did not move far enough")
670
pos = self.assert_receive_message('GLOBAL_POSITION_INT')
671
delta = self.get_distance_int(startpos, pos)
672
self.progress("delta=%f (want >10)" % delta)
673
if delta > 10:
674
break
675
self.change_mode('MANUAL')
676
self.disarm_vehicle()
677
678
def DoubleCircle(self):
679
'''Test entering circle twice'''
680
self.change_mode('CIRCLE')
681
self.wait_ready_to_arm()
682
self.arm_vehicle()
683
self.change_mode('STABILIZE')
684
self.change_mode('CIRCLE')
685
self.disarm_vehicle()
686
687
def default_parameter_list(self):
688
ret = super(AutoTestSub, self).default_parameter_list()
689
ret["FS_GCS_ENABLE"] = 0 # FIXME
690
return ret
691
692
def MAV_CMD_NAV_LOITER_UNLIM(self):
693
'''test handling of MAV_CMD_NAV_LOITER_UNLIM received via mavlink'''
694
for cmd in self.run_cmd, self.run_cmd_int:
695
self.change_mode('CIRCLE')
696
cmd(mavutil.mavlink.MAV_CMD_NAV_LOITER_UNLIM)
697
self.assert_mode('POSHOLD')
698
699
def MAV_CMD_NAV_LAND(self):
700
'''test handling of MAV_CMD_NAV_LAND received via mavlink'''
701
for cmd in self.run_cmd, self.run_cmd_int:
702
self.change_mode('CIRCLE')
703
cmd(mavutil.mavlink.MAV_CMD_NAV_LAND)
704
self.assert_mode('SURFACE')
705
706
def MAV_CMD_MISSION_START(self):
707
'''test handling of MAV_CMD_MISSION_START received via mavlink'''
708
self.upload_simple_relhome_mission([
709
(mavutil.mavlink.MAV_CMD_NAV_WAYPOINT, 2000, 0, 0),
710
(mavutil.mavlink.MAV_CMD_NAV_RETURN_TO_LAUNCH, 0, 0, 0),
711
])
712
713
self.wait_ready_to_arm()
714
self.arm_vehicle()
715
for cmd in self.run_cmd, self.run_cmd_int:
716
self.change_mode('CIRCLE')
717
cmd(mavutil.mavlink.MAV_CMD_MISSION_START)
718
self.assert_mode('AUTO')
719
self.disarm_vehicle()
720
721
def MAV_CMD_DO_CHANGE_SPEED(self):
722
'''ensure vehicle changes speeds when DO_CHANGE_SPEED received'''
723
items = [
724
(mavutil.mavlink.MAV_CMD_NAV_WAYPOINT, 0, 0, -3), # Dive so we have constant drag
725
(mavutil.mavlink.MAV_CMD_NAV_WAYPOINT, 2000, 0, -1),
726
(mavutil.mavlink.MAV_CMD_NAV_RETURN_TO_LAUNCH, 0, 0, 0),
727
]
728
self.upload_simple_relhome_mission(items)
729
self.wait_ready_to_arm()
730
self.arm_vehicle()
731
self.run_cmd(mavutil.mavlink.MAV_CMD_MISSION_START)
732
self.progress("SENT MISSION START")
733
self.wait_mode('AUTO')
734
self.wait_current_waypoint(2) # wait after we finish diving to 3m
735
for run_cmd in self.run_cmd, self.run_cmd_int:
736
for speed in [1, 1.5, 0.5]:
737
run_cmd(mavutil.mavlink.MAV_CMD_DO_CHANGE_SPEED, p2=speed)
738
self.wait_groundspeed(speed-0.2, speed+0.2, minimum_duration=2, timeout=60)
739
self.disarm_vehicle()
740
741
def GPSForYaw(self):
742
'''Consume heading of NMEA GPS and propagate to ATTITUDE'''
743
744
# load parameters with gps for yaw and 10 degrees offset
745
self.load_default_params_file("sub-gps-for-yaw-nmea.parm")
746
self.reboot_sitl()
747
# wait for the vehicle to be ready
748
self.wait_ready_to_arm()
749
# make sure we are getting both GPS_RAW_INT and SIMSTATE
750
simstate_m = self.assert_receive_message("SIMSTATE")
751
real_yaw_deg = degrees(simstate_m.yaw)
752
expected_yaw_deg = mavextra.wrap_180(real_yaw_deg + 30) # offset in the param file, in degrees
753
# wait for GPS_RAW_INT to have a good fix
754
self.wait_gps_fix_type_gte(3, message_type="GPS_RAW_INT", verbose=True)
755
756
att_m = self.assert_receive_message("ATTITUDE")
757
achieved_yaw_deg = mavextra.wrap_180(degrees(att_m.yaw))
758
759
# ensure new reading propagated to ATTITUDE
760
try:
761
self.wait_heading(expected_yaw_deg)
762
except NotAchievedException as e:
763
raise NotAchievedException(
764
"Expected to get yaw consumed and at ATTITUDE (want %f got %f)" % (expected_yaw_deg, achieved_yaw_deg)
765
) from e
766
767
def _MAV_CMD_CONDITION_YAW(self, run_cmd):
768
self.arm_vehicle()
769
self.change_mode('GUIDED')
770
for angle in 5, 30, 60, 10:
771
angular_rate = 10
772
direction = 1
773
relative_or_absolute = 0
774
run_cmd(
775
mavutil.mavlink.MAV_CMD_CONDITION_YAW,
776
p1=angle,
777
p2=angular_rate,
778
p3=direction,
779
p4=relative_or_absolute, # 1 for relative, 0 for absolute
780
)
781
self.wait_heading(angle, minimum_duration=2)
782
783
self.start_subtest('Relative angle')
784
run_cmd(
785
mavutil.mavlink.MAV_CMD_CONDITION_YAW,
786
p1=0,
787
p2=10,
788
p3=1,
789
p4=0, # 1 for relative, 0 for absolute
790
)
791
self.wait_heading(0, minimum_duration=2)
792
run_cmd(
793
mavutil.mavlink.MAV_CMD_CONDITION_YAW,
794
p1=20,
795
p2=10,
796
p3=1,
797
p4=1, # 1 for relative, 0 for absolute
798
)
799
self.wait_heading(20, minimum_duration=2)
800
801
self.disarm_vehicle()
802
803
def MAV_CMD_CONDITION_YAW(self):
804
'''ensure vehicle yaws according to GCS command'''
805
self._MAV_CMD_CONDITION_YAW(self.run_cmd)
806
self._MAV_CMD_CONDITION_YAW(self.run_cmd_int)
807
808
def MAV_CMD_DO_REPOSITION(self):
809
"""Move vehicle using MAV_CMD_DO_REPOSITION"""
810
self.wait_ready_to_arm()
811
self.arm_vehicle()
812
813
# Dive so that rangefinder is in range, required for MAV_FRAME_GLOBAL_TERRAIN_ALT
814
start_altitude = -25
815
pwm = 1300 if self.get_altitude(relative=True) > start_altitude else 1700
816
self.set_rc(Joystick.Throttle, pwm)
817
self.wait_altitude(altitude_min=start_altitude-1, altitude_max=start_altitude, relative=False, timeout=120)
818
self.set_rc(Joystick.Throttle, 1500)
819
self.change_mode('GUIDED')
820
821
loc = self.mav.location()
822
823
# Reposition, alt relative to surface
824
loc = self.offset_location_ne(loc, 10, 10)
825
loc.alt = start_altitude
826
self.send_do_reposition(loc, frame=mavutil.mavlink.MAV_FRAME_GLOBAL_RELATIVE_ALT)
827
self.wait_location(loc, timeout=120)
828
829
# Reposition, alt relative to seafloor
830
loc = self.offset_location_ne(loc, 10, 10)
831
loc.alt = -start_altitude
832
self.send_do_reposition(loc, frame=mavutil.mavlink.MAV_FRAME_GLOBAL_TERRAIN_ALT)
833
self.wait_location(loc, timeout=120)
834
835
self.disarm_vehicle()
836
837
def TerrainMission(self):
838
"""Mission using surface tracking"""
839
840
self.assert_parameter_value('RNGFND1_MAX', 30)
841
842
filename = "terrain_mission.txt"
843
self.progress("Executing mission %s" % filename)
844
self.load_mission(filename)
845
self.set_rc_default()
846
self.arm_vehicle()
847
self.change_mode('AUTO')
848
self.wait_waypoint(1, 4, max_dist=5)
849
self.delay_sim_time(3)
850
851
# Expect sub to hover at final altitude
852
self.assert_altitude(-36.0)
853
854
self.disarm_vehicle()
855
self.progress("Mission OK")
856
857
def wait_ekf_happy_const_pos(self, timeout=45):
858
# All of these must be set for arming to happen in constant position mode:
859
required_value = (mavutil.mavlink.EKF_ATTITUDE |
860
mavutil.mavlink.EKF_VELOCITY_HORIZ |
861
mavutil.mavlink.EKF_VELOCITY_VERT |
862
mavutil.mavlink.EKF_POS_VERT_ABS |
863
mavutil.mavlink.EKF_CONST_POS_MODE)
864
865
# None of these bits must be set for arming to happen:
866
error_bits = mavutil.mavlink.EKF_UNINITIALIZED
867
868
self.wait_ekf_flags(required_value, error_bits, timeout=timeout)
869
870
def wait_ready_to_arm_const_pos(self, timeout=120):
871
self.progress("Waiting for ready to arm (constant position mode)")
872
start = self.get_sim_time()
873
self.wait_ekf_happy_const_pos(timeout=timeout)
874
armable_time = self.get_sim_time() - start
875
self.progress("Took %u seconds to become armable" % armable_time)
876
self.total_waiting_to_arm_time += armable_time
877
self.waiting_to_arm_count += 1
878
879
def collected_msgs(self, msg_type):
880
c = self.context_get()
881
if msg_type not in c.collections:
882
raise NotAchievedException("Not collecting (%s)" % str(msg_type))
883
return c.collections[msg_type]
884
885
def SetGlobalOrigin(self):
886
"""Test SET_GPS_GLOBAL_ORIGIN mav msg"""
887
self.context_push()
888
self.set_parameters({
889
'GPS1_TYPE': 0, # Disable the GPS
890
'EK3_SRC1_POSXY': 0, # Make sure EK3_SRC parameters do not refer to a GPS
891
})
892
self.reboot_sitl()
893
894
# Wait for the EKF to be happy in constant position mode
895
self.wait_ready_to_arm_const_pos()
896
897
if self.current_onboard_log_contains_message('ORGN'):
898
raise NotAchievedException("Found unexpected ORGN message")
899
900
self.context_collect('GPS_GLOBAL_ORIGIN')
901
902
# This should set the EKF origin, write an ORGN msg to df and a GPS_GLOBAL_ORIGIN msg to MAVLink
903
self.mav.mav.set_gps_global_origin_send(1, int(47.607584 * 1e7), int(-122.343911 * 1e7), 0)
904
self.delay_sim_time(2)
905
906
if not self.current_onboard_log_contains_message('ORGN'):
907
raise NotAchievedException("Did not find expected ORGN message")
908
909
num_mavlink_origin_msgs = len(self.collected_msgs('GPS_GLOBAL_ORIGIN'))
910
if num_mavlink_origin_msgs != 1:
911
raise NotAchievedException("Expected 1 GPS_GLOBAL_ORIGIN message, found %d" % num_mavlink_origin_msgs)
912
913
self.context_pop()
914
915
# restart GPS driver
916
self.reboot_sitl()
917
918
def BackupOrigin(self):
919
"""Test AHRS_ORIGIN_LAT and AHRS_ORIGIN_LON parameters"""
920
921
self.context_push()
922
self.set_parameters({
923
'GPS1_TYPE': 0, # Disable GPS
924
'EK3_SRC1_POSXY': 0, # Make sure EK3_SRC parameters do not refer to GPS
925
'EK3_SRC1_VELXY': 0, # Make sure EK3_SRC parameters do not refer to GPS
926
'AHRS_ORIGIN_LAT': 47.607584,
927
'AHRS_ORIGIN_LON': -122.343911,
928
})
929
self.reboot_sitl()
930
self.context_collect('STATUSTEXT')
931
932
# Wait for the EKF to be happy in constant position mode
933
self.wait_ready_to_arm_const_pos()
934
935
self.wait_statustext('AHRS: using recorded origin', check_context=True)
936
937
# check that origin has been logged
938
if not self.current_onboard_log_contains_message('ORGN'):
939
raise NotAchievedException("Did not find expected ORGN message")
940
941
self.context_pop()
942
943
def assert_mag_fusion_selection(self, expect_sel):
944
"""Get the most recent XKFS message and check the MAG_FUSION value"""
945
self.progress("Expect mag fusion selection %d" % expect_sel)
946
mlog = self.dfreader_for_current_onboard_log()
947
found_sel = MagFuseSel.NOT_FUSING
948
while True:
949
m = mlog.recv_match(type='XKFS')
950
if m is None:
951
break
952
found_sel = m.MAG_FUSION
953
if found_sel != expect_sel:
954
raise NotAchievedException("Expected mag fusion selection %d, found %d" % (expect_sel, found_sel))
955
956
def FuseMag(self):
957
"""Test EK3_MAG_CAL values"""
958
959
# WHEN_FLYING: switch to FUSE_MAG after sub is armed for 5 seconds; switch to FUSE_YAW on disarm
960
self.set_parameters({'EK3_MAG_CAL': MagCal.WHEN_FLYING})
961
self.reboot_sitl()
962
self.wait_ready_to_arm()
963
self.assert_mag_fusion_selection(MagFuseSel.FUSE_YAW)
964
self.arm_vehicle()
965
self.delay_sim_time(10)
966
self.assert_mag_fusion_selection(MagFuseSel.FUSE_MAG)
967
self.disarm_vehicle()
968
self.delay_sim_time(1)
969
self.assert_mag_fusion_selection(MagFuseSel.FUSE_YAW)
970
971
# AFTER_FIRST_CLIMB: switch to FUSE_MAG after sub is armed and descends 0.5m; switch to FUSE_YAW on disarm
972
self.set_parameters({'EK3_MAG_CAL': MagCal.AFTER_FIRST_CLIMB})
973
self.reboot_sitl()
974
self.wait_ready_to_arm()
975
self.assert_mag_fusion_selection(MagFuseSel.FUSE_YAW)
976
altitude = self.get_altitude(relative=True)
977
self.arm_vehicle()
978
self.set_rc(Joystick.Throttle, 1300)
979
self.wait_altitude(altitude_min=altitude-4, altitude_max=altitude-3, relative=False, timeout=60)
980
self.set_rc(Joystick.Throttle, 1500)
981
self.assert_mag_fusion_selection(MagFuseSel.FUSE_MAG)
982
self.disarm_vehicle()
983
self.delay_sim_time(1)
984
self.assert_mag_fusion_selection(MagFuseSel.FUSE_YAW)
985
986
# ALWAYS
987
self.set_parameters({'EK3_MAG_CAL': MagCal.ALWAYS})
988
self.reboot_sitl()
989
self.wait_ready_to_arm()
990
self.assert_mag_fusion_selection(MagFuseSel.FUSE_MAG)
991
992
def INA3221(self):
993
'''test INA3221 driver'''
994
self.set_parameters({
995
"BATT2_MONITOR": 30,
996
"BATT3_MONITOR": 30,
997
"BATT4_MONITOR": 30,
998
})
999
self.reboot_sitl()
1000
self.set_parameters({
1001
"BATT2_I2C_ADDR": 0x42, # address defined in libraries/SITL/SIM_I2C.cpp
1002
"BATT2_I2C_BUS": 1,
1003
"BATT2_CHANNEL": 1,
1004
1005
"BATT3_I2C_ADDR": 0x42,
1006
"BATT3_I2C_BUS": 1,
1007
"BATT3_CHANNEL": 2,
1008
1009
"BATT4_I2C_ADDR": 0x42,
1010
"BATT4_I2C_BUS": 1,
1011
"BATT4_CHANNEL": 3,
1012
})
1013
self.reboot_sitl()
1014
1015
seen_1 = False
1016
seen_3 = False
1017
tstart = self.get_sim_time()
1018
while not (seen_1 and seen_3):
1019
m = self.assert_receive_message('BATTERY_STATUS')
1020
if self.get_sim_time() - tstart > 10:
1021
# expected to take under 1 simulated second, but don't hang if
1022
# e.g. the driver gets stuck
1023
raise NotAchievedException("INA3221 status timeout")
1024
if m.id == 1:
1025
self.assert_message_field_values(m, {
1026
# values close to chip limits
1027
"voltages[0]": int(25 * 1000), # millivolts
1028
"current_battery": int(160 * 100), # centi-amps
1029
}, epsilon=10) # allow rounding
1030
seen_1 = True
1031
# id 2 is the first simulated battery current/voltage
1032
if m.id == 3:
1033
self.assert_message_field_values(m, {
1034
# values different from above to test channel switching
1035
"voltages[0]": int(3.14159 * 1000), # millivolts
1036
"current_battery": int(2.71828 * 100), # centi-amps
1037
}, epsilon=10) # allow rounding
1038
seen_3 = True
1039
1040
def wait_for_stop(self):
1041
"""Watch the sub slow down and stop"""
1042
tstart = self.get_sim_time_cached()
1043
lstart = self.mav.location()
1044
1045
dmax = 0
1046
dprev = 0
1047
1048
while True:
1049
self.delay_sim_time(1)
1050
1051
dcurr = self.get_distance(lstart, self.mav.location())
1052
1053
if dcurr - dmax < -0.2:
1054
raise NotAchievedException("Bounced back from %.2fm to %.2fm" % (dmax, dcurr))
1055
if dcurr > dmax:
1056
dmax = dcurr
1057
1058
if abs(dcurr - dprev) < 0.1:
1059
self.progress("Stopping distance %.2fm, less than %.2fs" % (dcurr, self.get_sim_time_cached() - tstart))
1060
return
1061
1062
if self.get_sim_time_cached() - tstart > 10:
1063
raise NotAchievedException("Took to long to stop")
1064
1065
dprev = dcurr
1066
1067
def PosHoldBounceBack(self):
1068
"""Test for bounce back in POSHOLD mode"""
1069
self.wait_ready_to_arm()
1070
self.arm_vehicle()
1071
1072
# dive a little
1073
self.set_rc(Joystick.Throttle, 1300)
1074
self.delay_sim_time(3)
1075
self.set_rc(Joystick.Throttle, 1500)
1076
self.delay_sim_time(2)
1077
1078
# hold position
1079
self.change_mode('POSHOLD')
1080
1081
for pilot_speed in range(50, 251, 100):
1082
# set max speed
1083
self.set_parameter('PILOT_SPEED', pilot_speed)
1084
1085
# try different stick values, resulting speed is ~ max_speed * effort * gain
1086
for pwm in range(1700, 1901, 100):
1087
self.progress('PILOT_SPEED %d, forward pwm %d' % (pilot_speed, pwm))
1088
self.set_rc(Joystick.Forward, pwm)
1089
self.delay_sim_time(3)
1090
self.set_rc(Joystick.Forward, 1500)
1091
self.wait_for_stop()
1092
1093
self.disarm_vehicle()
1094
1095
def SHT3X(self):
1096
'''test for the SHT3X temperature/hygro driver'''
1097
self.set_parameters({
1098
'TEMP1_TYPE': 8,
1099
'TEMP1_ADDR': 0x44,
1100
'TEMP_LOG': 1,
1101
})
1102
self.reboot_sitl()
1103
self.context_push()
1104
self.set_parameter('LOG_DISARMED', 1)
1105
self.delay_sim_time(10)
1106
self.context_pop()
1107
1108
dfreader = self.dfreader_for_current_onboard_log()
1109
while True:
1110
m = dfreader.recv_match(type='TEMP')
1111
if m is None:
1112
break
1113
self.progress(m)
1114
if m.Temp > 15 or m.Temp < 30:
1115
# success!
1116
break
1117
if m is None:
1118
raise NotAchievedException("Did not get good TEMP message")
1119
1120
def MAV_mgs(self):
1121
'''test individual GCS backends timestamps'''
1122
self.reboot_sitl()
1123
self.set_parameter("MAV_GCS_SYSID", self.mav.source_system)
1124
self.delay_sim_time(10, reason='add delay on connecting "telemetry')
1125
1126
self.progress("Connecting to telemetry port")
1127
mav2 = mavutil.mavlink_connection(
1128
"tcp:localhost:5763",
1129
robust_parsing=True,
1130
source_system=self.mav.source_system,
1131
source_component=self.mav.source_component,
1132
)
1133
tstart = self.get_sim_time()
1134
while True:
1135
tnow = self.get_sim_time()
1136
self.drain_mav()
1137
if tnow - tstart > 20:
1138
break
1139
if tnow - tstart > 1:
1140
mav2.mav.heartbeat_send(
1141
mavutil.mavlink.MAV_TYPE_ONBOARD_CONTROLLER,
1142
mavutil.mavlink.MAV_AUTOPILOT_INVALID,
1143
0,
1144
0,
1145
0,
1146
)
1147
self.delay_sim_time(20, reason="allow for checking not receiving hb any more on chan=2")
1148
dfreader = self.dfreader_for_current_onboard_log()
1149
1150
chan0_count = 0
1151
chan0_last_timestamp_us = 0
1152
chan0_last_mgs = 0
1153
chan2_count = 0
1154
chan2_last_timestamp_us = 0
1155
chan2_last_mgs = 0
1156
att_ts_us = 0
1157
while True:
1158
m = dfreader.recv_match(type=['MAV', 'ATT'])
1159
if m is None:
1160
raise NotAchievedException("Did not find everything wanted in log")
1161
if chan2_count > 10:
1162
self.progress("Received 10 heartbeats on chan==2")
1163
break
1164
if m.get_type() == 'ATT':
1165
att_ts_us = m.TimeUS
1166
continue
1167
if m.mgs == 0:
1168
# no heartbeat received yet
1169
continue
1170
if m.chan == 0:
1171
if chan0_count == 0:
1172
if att_ts_us > 5000000:
1173
raise NotAchievedException(f"Late arrival on chan=0 {att_ts_us=}")
1174
chan0_count += 1
1175
if chan0_count > 3:
1176
if att_ts_us - chan0_last_timestamp_us > 2000000:
1177
raise NotAchievedException(f"Unexpected interval on chan=0 {att_ts_us=} {chan0_last_timestamp_us=}")
1178
if m.mgs - chan0_last_mgs > 2000:
1179
raise NotAchievedException(f"Unexpected interval on chan==0 mgs {m.mgs=} {chan0_last_mgs=}")
1180
chan0_last_mgs = m.mgs
1181
chan0_last_timestamp_us = att_ts_us
1182
elif m.chan == 2:
1183
if chan2_count == 0:
1184
if att_ts_us < 10000000:
1185
raise NotAchievedException(f"Early heartbeat on chan==2 {att_ts_us=}")
1186
chan2_count += 1
1187
if chan2_count > 1:
1188
if att_ts_us - chan2_last_timestamp_us > 2000000:
1189
raise NotAchievedException("Unexpected interval on chan=0")
1190
if m.mgs - chan2_last_mgs > 2000:
1191
raise NotAchievedException(f"Unexpected interval on chan==0 mgs {m.mgs=} {chan2_last_mgs=}")
1192
chan2_last_mgs = m.mgs
1193
chan2_last_timestamp_us = att_ts_us
1194
1195
self.progress("Waiting for heartbeats to stop on chan==2")
1196
chan0_last_timestamp_us = 0
1197
chan2_last_timestamp_us = 0
1198
while True:
1199
m = dfreader.recv_match(type=['MAV', 'ATT'])
1200
if m is None:
1201
raise NotAchievedException("heartbeats did not stop on chan==2")
1202
1203
if m.get_type() == 'ATT':
1204
att_ts_us = m.TimeUS
1205
continue
1206
if m.chan == 0:
1207
chan0_last_timestamp_us = att_ts_us
1208
if chan0_last_timestamp_us - chan2_last_timestamp_us > 5000000:
1209
self.progress("chan==2 heartbeats have stopped")
1210
break
1211
elif m.chan == 2:
1212
chan2_last_timestamp_us = att_ts_us
1213
1214
def SurfaceSensorless(self):
1215
"""Test surface mode with sensorless thrust"""
1216
# set GCS failsafe to SURFACE
1217
self.wait_ready_to_arm()
1218
self.arm_vehicle()
1219
self.change_mode("STABILIZE")
1220
self.set_parameter("MAV_GCS_SYSID", self.mav.source_system)
1221
1222
self.set_rc(Joystick.Throttle, 1100)
1223
self.wait_altitude(altitude_min=-10, altitude_max=-9, relative=False, timeout=60)
1224
self.set_rc(Joystick.Throttle, 1500)
1225
1226
self.context_push()
1227
self.setGCSfailsafe(4)
1228
self.set_parameter("SIM_BARO_DISABLE", 1)
1229
self.set_heartbeat_rate(0)
1230
self.wait_mode("SURFACE")
1231
self.progress("Surface mode engaged")
1232
self.wait_altitude(altitude_min=-1, altitude_max=0, relative=False, timeout=60)
1233
self.progress("Vehicle resurfaced")
1234
self.set_heartbeat_rate(self.speedup)
1235
self.wait_statustext("GCS Failsafe Cleared", timeout=60)
1236
self.progress("Baro-less Surface mode OK")
1237
self.disarm_vehicle()
1238
self.context_pop()
1239
1240
def tests(self):
1241
'''return list of all tests'''
1242
ret = super(AutoTestSub, self).tests()
1243
1244
ret.extend([
1245
self.DiveManual,
1246
self.GCSFailsafe,
1247
self.ThrottleFailsafe,
1248
self.AltitudeHold,
1249
self.Surftrak,
1250
self.SimTerrainSurftrak,
1251
self.SimTerrainMission,
1252
self.RngfndQuality,
1253
self.PositionHold,
1254
self.ModeChanges,
1255
self.MAV_mgs,
1256
self.DiveMission,
1257
self.GripperMission,
1258
self.DoubleCircle,
1259
self.MotorThrustHoverParameterIgnore,
1260
self.SET_POSITION_TARGET_GLOBAL_INT,
1261
self.TestLogDownloadMAVProxy,
1262
self.TestLogDownloadMAVProxyNetwork,
1263
self.TestLogDownloadLogRestart,
1264
self.MAV_CMD_NAV_LOITER_UNLIM,
1265
self.MAV_CMD_NAV_LAND,
1266
self.MAV_CMD_MISSION_START,
1267
self.MAV_CMD_DO_CHANGE_SPEED,
1268
self.MAV_CMD_CONDITION_YAW,
1269
self.MAV_CMD_DO_REPOSITION,
1270
self.TerrainMission,
1271
self.SetGlobalOrigin,
1272
self.BackupOrigin,
1273
self.FuseMag,
1274
self.INA3221,
1275
self.PosHoldBounceBack,
1276
self.SHT3X,
1277
self.SurfaceSensorless,
1278
self.GPSForYaw,
1279
])
1280
1281
return ret
1282
1283