CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
Ardupilot

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.

GitHub Repository: Ardupilot/ardupilot
Path: blob/master/Tools/autotest/arduplane.py
Views: 1798
1
'''
2
Fly ArduPlane in SITL
3
4
AP_FLAKE8_CLEAN
5
'''
6
7
from __future__ import print_function
8
import copy
9
import math
10
import os
11
import signal
12
13
from pymavlink import quaternion
14
from pymavlink import mavutil
15
16
from pymavlink.rotmat import Vector3
17
18
import vehicle_test_suite
19
20
from vehicle_test_suite import AutoTestTimeoutException
21
from vehicle_test_suite import NotAchievedException
22
from vehicle_test_suite import OldpymavlinkException
23
from vehicle_test_suite import PreconditionFailedException
24
from vehicle_test_suite import Test
25
from vehicle_test_suite import WaitModeTimeout
26
27
from pysim import vehicleinfo
28
from pysim import util
29
30
import operator
31
32
# get location of scripts
33
testdir = os.path.dirname(os.path.realpath(__file__))
34
SITL_START_LOCATION = mavutil.location(-35.362938, 149.165085, 585, 354)
35
WIND = "0,180,0.2" # speed,direction,variance
36
37
38
class AutoTestPlane(vehicle_test_suite.TestSuite):
39
@staticmethod
40
def get_not_armable_mode_list():
41
return []
42
43
@staticmethod
44
def get_not_disarmed_settable_modes_list():
45
return ["FOLLOW"]
46
47
@staticmethod
48
def get_no_position_not_settable_modes_list():
49
return []
50
51
@staticmethod
52
def get_position_armable_modes_list():
53
return ["GUIDED", "AUTO"]
54
55
@staticmethod
56
def get_normal_armable_modes_list():
57
return ["MANUAL", "STABILIZE", "ACRO"]
58
59
def log_name(self):
60
return "ArduPlane"
61
62
def default_speedup(self):
63
return 100
64
65
def test_filepath(self):
66
return os.path.realpath(__file__)
67
68
def sitl_start_location(self):
69
return SITL_START_LOCATION
70
71
def defaults_filepath(self):
72
return os.path.join(testdir, 'default_params/plane-jsbsim.parm')
73
74
def set_current_test_name(self, name):
75
self.current_test_name_directory = "ArduPlane_Tests/" + name + "/"
76
77
def default_frame(self):
78
return "plane-elevrev"
79
80
def apply_defaultfile_parameters(self):
81
# plane passes in a defaults_filepath in place of applying
82
# parameters afterwards.
83
pass
84
85
def is_plane(self):
86
return True
87
88
def get_stick_arming_channel(self):
89
return int(self.get_parameter("RCMAP_YAW"))
90
91
def get_disarm_delay(self):
92
return int(self.get_parameter("LAND_DISARMDELAY"))
93
94
def set_autodisarm_delay(self, delay):
95
self.set_parameter("LAND_DISARMDELAY", delay)
96
97
def takeoff(self, alt=150, alt_max=None, relative=True, mode=None, timeout=None):
98
"""Takeoff to altitude."""
99
100
if mode == "TAKEOFF":
101
return self.takeoff_in_TAKEOFF(alt=alt, relative=relative, timeout=timeout)
102
103
return self.takeoff_in_FBWA(alt=alt, alt_max=alt_max, relative=relative, timeout=timeout)
104
105
def takeoff_in_TAKEOFF(self, alt=150, relative=True, mode=None, alt_epsilon=2, timeout=None):
106
if relative is not True:
107
raise ValueError("Only relative alt supported ATM")
108
self.change_mode("TAKEOFF")
109
self.context_push()
110
self.set_parameter('TKOFF_ALT', alt)
111
self.wait_ready_to_arm()
112
self.arm_vehicle()
113
self.wait_altitude(alt-alt_epsilon, alt+alt_epsilon, relative=True, timeout=timeout)
114
self.context_pop()
115
116
def takeoff_in_FBWA(self, alt=150, alt_max=None, relative=True, mode=None, timeout=30):
117
if alt_max is None:
118
alt_max = alt + 30
119
120
self.change_mode("FBWA")
121
122
self.wait_ready_to_arm()
123
self.arm_vehicle()
124
125
# some rudder to counteract the prop torque
126
self.set_rc(4, 1700)
127
128
# some up elevator to keep the tail down
129
self.set_rc(2, 1200)
130
131
# get it moving a bit first
132
self.set_rc(3, 1300)
133
self.wait_groundspeed(6, 100)
134
135
# a bit faster again, straighten rudder
136
self.set_rc_from_map({
137
3: 1600,
138
4: 1500,
139
})
140
self.wait_groundspeed(12, 100)
141
142
# hit the gas harder now, and give it some more elevator
143
self.set_rc_from_map({
144
2: 1100,
145
3: 2000,
146
})
147
148
# gain a bit of altitude
149
self.wait_altitude(alt, alt_max, timeout=timeout, relative=relative)
150
151
# level off
152
self.set_rc(2, 1500)
153
154
self.progress("TAKEOFF COMPLETE")
155
156
def fly_left_circuit(self):
157
"""Fly a left circuit, 200m on a side."""
158
self.change_mode('FBWA')
159
self.set_rc(3, 2000)
160
self.wait_level_flight()
161
162
self.progress("Flying left circuit")
163
# do 4 turns
164
for i in range(0, 4):
165
# hard left
166
self.progress("Starting turn %u" % i)
167
self.set_rc(1, 1000)
168
self.wait_heading(270 - (90*i), accuracy=10)
169
self.set_rc(1, 1500)
170
self.progress("Starting leg %u" % i)
171
self.wait_distance(100, accuracy=20)
172
self.progress("Circuit complete")
173
174
def fly_RTL(self):
175
"""Fly to home."""
176
self.progress("Flying home in RTL")
177
target_loc = self.home_position_as_mav_location()
178
target_loc.alt += 100
179
self.change_mode('RTL')
180
self.wait_location(target_loc,
181
accuracy=120,
182
height_accuracy=20,
183
timeout=180)
184
self.progress("RTL Complete")
185
186
def NeedEKFToArm(self):
187
"""Ensure the EKF must be healthy for the vehicle to arm."""
188
self.progress("Ensuring we need EKF to be healthy to arm")
189
self.set_parameter("SIM_GPS1_ENABLE", 0)
190
self.context_collect("STATUSTEXT")
191
tstart = self.get_sim_time()
192
success = False
193
for run_cmd in self.run_cmd, self.run_cmd_int:
194
while not success:
195
if self.get_sim_time_cached() - tstart > 60:
196
raise NotAchievedException("Did not get correct failure reason")
197
run_cmd(mavutil.mavlink.MAV_CMD_RUN_PREARM_CHECKS)
198
try:
199
self.wait_statustext(".*AHRS: not using configured AHRS type.*", timeout=1, check_context=True, regex=True)
200
success = True
201
continue
202
except AutoTestTimeoutException:
203
pass
204
205
self.set_parameter("SIM_GPS1_ENABLE", 1)
206
self.wait_ready_to_arm()
207
208
def fly_LOITER(self, num_circles=4):
209
"""Loiter where we are."""
210
self.progress("Testing LOITER for %u turns" % num_circles)
211
self.change_mode('LOITER')
212
213
m = self.mav.recv_match(type='VFR_HUD', blocking=True)
214
initial_alt = m.alt
215
self.progress("Initial altitude %u\n" % initial_alt)
216
217
while num_circles > 0:
218
self.wait_heading(0, accuracy=10, timeout=60)
219
self.wait_heading(180, accuracy=10, timeout=60)
220
num_circles -= 1
221
self.progress("Loiter %u circles left" % num_circles)
222
223
m = self.mav.recv_match(type='VFR_HUD', blocking=True)
224
final_alt = m.alt
225
self.progress("Final altitude %u initial %u\n" %
226
(final_alt, initial_alt))
227
228
self.change_mode('FBWA')
229
230
if abs(final_alt - initial_alt) > 20:
231
raise NotAchievedException("Failed to maintain altitude")
232
233
self.progress("Completed Loiter OK")
234
235
def fly_CIRCLE(self, num_circles=1):
236
"""Circle where we are."""
237
self.progress("Testing CIRCLE for %u turns" % num_circles)
238
self.change_mode('CIRCLE')
239
240
m = self.mav.recv_match(type='VFR_HUD', blocking=True)
241
initial_alt = m.alt
242
self.progress("Initial altitude %u\n" % initial_alt)
243
244
while num_circles > 0:
245
self.wait_heading(0, accuracy=10, timeout=60)
246
self.wait_heading(180, accuracy=10, timeout=60)
247
num_circles -= 1
248
self.progress("CIRCLE %u circles left" % num_circles)
249
250
m = self.mav.recv_match(type='VFR_HUD', blocking=True)
251
final_alt = m.alt
252
self.progress("Final altitude %u initial %u\n" %
253
(final_alt, initial_alt))
254
255
self.change_mode('FBWA')
256
257
if abs(final_alt - initial_alt) > 20:
258
raise NotAchievedException("Failed to maintain altitude")
259
260
self.progress("Completed CIRCLE OK")
261
262
def wait_level_flight(self, accuracy=5, timeout=30):
263
"""Wait for level flight."""
264
tstart = self.get_sim_time()
265
self.progress("Waiting for level flight")
266
self.set_rc(1, 1500)
267
self.set_rc(2, 1500)
268
self.set_rc(4, 1500)
269
while self.get_sim_time_cached() < tstart + timeout:
270
m = self.mav.recv_match(type='ATTITUDE', blocking=True)
271
roll = math.degrees(m.roll)
272
pitch = math.degrees(m.pitch)
273
self.progress("Roll=%.1f Pitch=%.1f" % (roll, pitch))
274
if math.fabs(roll) <= accuracy and math.fabs(pitch) <= accuracy:
275
self.progress("Attained level flight")
276
return
277
raise NotAchievedException("Failed to attain level flight")
278
279
def change_altitude(self, altitude, accuracy=30, relative=False):
280
"""Get to a given altitude."""
281
if relative:
282
altitude += self.home_position_as_mav_location().alt
283
self.change_mode('FBWA')
284
alt_error = self.mav.messages['VFR_HUD'].alt - altitude
285
if alt_error > 0:
286
self.set_rc(2, 2000)
287
else:
288
self.set_rc(2, 1000)
289
self.wait_altitude(altitude-accuracy/2, altitude+accuracy/2)
290
self.set_rc(2, 1500)
291
self.progress("Reached target altitude at %u" %
292
self.mav.messages['VFR_HUD'].alt)
293
return self.wait_level_flight()
294
295
def axial_left_roll(self, count=1):
296
"""Fly a left axial roll."""
297
# full throttle!
298
self.set_rc(3, 2000)
299
self.change_altitude(300, relative=True)
300
301
# fly the roll in manual
302
self.change_mode('MANUAL')
303
304
while count > 0:
305
self.progress("Starting roll")
306
self.set_rc(1, 1000)
307
try:
308
self.wait_roll(-150, accuracy=90)
309
self.wait_roll(150, accuracy=90)
310
self.wait_roll(0, accuracy=90)
311
except Exception as e:
312
self.set_rc(1, 1500)
313
raise e
314
count -= 1
315
316
# back to FBWA
317
self.set_rc(1, 1500)
318
self.change_mode('FBWA')
319
self.set_rc(3, 1700)
320
return self.wait_level_flight()
321
322
def inside_loop(self, count=1):
323
"""Fly a inside loop."""
324
# full throttle!
325
self.set_rc(3, 2000)
326
self.change_altitude(300, relative=True)
327
# fly the loop in manual
328
self.change_mode('MANUAL')
329
330
while count > 0:
331
self.progress("Starting loop")
332
self.set_rc(2, 1000)
333
self.wait_pitch(-60, accuracy=20)
334
self.wait_pitch(0, accuracy=20)
335
count -= 1
336
337
# back to FBWA
338
self.set_rc(2, 1500)
339
self.change_mode('FBWA')
340
self.set_rc(3, 1700)
341
return self.wait_level_flight()
342
343
def set_attitude_target(self, tolerance=10):
344
"""Test setting of attitude target in guided mode."""
345
self.change_mode("GUIDED")
346
347
steps = [{"name": "roll-over", "roll": 60, "pitch": 0, "yaw": 0, "throttle": 0, "type_mask": 0b10000001},
348
{"name": "roll-back", "roll": 0, "pitch": 0, "yaw": 0, "throttle": 0, "type_mask": 0b10000001},
349
{"name": "pitch-up+throttle", "roll": 0, "pitch": 20, "yaw": 0, "throttle": 1, "type_mask": 0b11000010},
350
{"name": "pitch-back", "roll": 0, "pitch": 0, "yaw": 0, "throttle": 0, "type_mask": 0b10000010}]
351
352
state_wait = "wait"
353
state_hold = "hold"
354
try:
355
for step in steps:
356
step_start = self.get_sim_time_cached()
357
state = state_wait
358
state_start = self.get_sim_time_cached()
359
while True:
360
m = self.mav.recv_match(type='ATTITUDE',
361
blocking=True,
362
timeout=0.1)
363
now = self.get_sim_time_cached()
364
if now - step_start > 30:
365
raise AutoTestTimeoutException("Manuevers not completed")
366
if m is None:
367
continue
368
369
angle_error = 0
370
if (step["type_mask"] & 0b00000001) or (step["type_mask"] == 0b10000000):
371
angle_error += abs(math.degrees(m.roll) - step["roll"])
372
373
if (step["type_mask"] & 0b00000010) or (step["type_mask"] == 0b10000000):
374
angle_error += abs(math.degrees(m.pitch) - step["pitch"])
375
376
if (step["type_mask"] & 0b00000100) or (step["type_mask"] == 0b10000000):
377
# Strictly we should angle wrap, by plane doesn't support yaw correctly anyway so its not tested here
378
angle_error += abs(math.degrees(m.yaw) - step["yaw"])
379
380
# Note were not checking throttle, however the SITL plane needs full throttle to meet the
381
# target pitch attitude, Pitch test will fail without throttle override
382
383
if state == state_wait:
384
# Reduced tolerance for initial trigger
385
if angle_error < (tolerance * 0.25):
386
state = state_hold
387
state_start = now
388
389
# Allow 10 seconds to reach attitude
390
if (now - state_start) > 10:
391
raise NotAchievedException(step["name"] + ": Failed to get to set attitude")
392
393
elif state == state_hold:
394
# Give 2 seconds to stabilize
395
if (now - state_start) > 2 and not (angle_error < tolerance):
396
raise NotAchievedException(step["name"] + ": Failed to hold set attitude")
397
398
# Hold for 10 seconds
399
if (now - state_start) > 12:
400
# move onto next step
401
self.progress("%s Done" % (step["name"]))
402
break
403
404
self.progress("%s %s error: %f" % (step["name"], state, angle_error))
405
406
time_boot_millis = 0 # FIXME
407
target_system = 1 # FIXME
408
target_component = 1 # FIXME
409
type_mask = step["type_mask"] ^ 0xFF # FIXME
410
# attitude in radians:
411
q = quaternion.Quaternion([math.radians(step["roll"]),
412
math.radians(step["pitch"]),
413
math.radians(step["yaw"])])
414
self.mav.mav.set_attitude_target_send(time_boot_millis,
415
target_system,
416
target_component,
417
type_mask,
418
q,
419
0, # roll rate, not used in AP
420
0, # pitch rate, not used in AP
421
0, # yaw rate, not used in AP
422
step["throttle"])
423
except Exception as e:
424
self.change_mode('FBWA')
425
self.set_rc(3, 1700)
426
raise e
427
428
# back to FBWA
429
self.change_mode('FBWA')
430
self.set_rc(3, 1700)
431
self.wait_level_flight()
432
433
def test_stabilize(self, count=1):
434
"""Fly stabilize mode."""
435
# full throttle!
436
self.set_rc(3, 2000)
437
self.set_rc(2, 1300)
438
self.change_altitude(300, relative=True)
439
self.set_rc(2, 1500)
440
441
self.change_mode('STABILIZE')
442
443
while count > 0:
444
self.progress("Starting roll")
445
self.set_rc(1, 2000)
446
self.wait_roll(-150, accuracy=90)
447
self.wait_roll(150, accuracy=90)
448
self.wait_roll(0, accuracy=90)
449
count -= 1
450
451
self.set_rc(1, 1500)
452
self.wait_roll(0, accuracy=5)
453
454
# back to FBWA
455
self.change_mode('FBWA')
456
self.set_rc(3, 1700)
457
return self.wait_level_flight()
458
459
def test_acro(self, count=1):
460
"""Fly ACRO mode."""
461
# full throttle!
462
self.set_rc(3, 2000)
463
self.set_rc(2, 1300)
464
self.change_altitude(300, relative=True)
465
self.set_rc(2, 1500)
466
467
self.change_mode('ACRO')
468
469
while count > 0:
470
self.progress("Starting roll")
471
self.set_rc(1, 1000)
472
self.wait_roll(-150, accuracy=90)
473
self.wait_roll(150, accuracy=90)
474
self.wait_roll(0, accuracy=90)
475
count -= 1
476
self.set_rc(1, 1500)
477
478
# back to FBWA
479
self.change_mode('FBWA')
480
481
self.wait_level_flight()
482
483
self.change_mode('ACRO')
484
485
count = 2
486
while count > 0:
487
self.progress("Starting loop")
488
self.set_rc(2, 1000)
489
self.wait_pitch(-60, accuracy=20)
490
self.wait_pitch(0, accuracy=20)
491
count -= 1
492
493
self.set_rc(2, 1500)
494
495
# back to FBWA
496
self.change_mode('FBWA')
497
self.set_rc(3, 1700)
498
return self.wait_level_flight()
499
500
def test_FBWB(self, mode='FBWB'):
501
"""Fly FBWB or CRUISE mode."""
502
self.change_mode(mode)
503
self.set_rc(3, 1700)
504
self.set_rc(2, 1500)
505
506
# lock in the altitude by asking for an altitude change then releasing
507
self.set_rc(2, 1000)
508
self.wait_distance(50, accuracy=20)
509
self.set_rc(2, 1500)
510
self.wait_distance(50, accuracy=20)
511
512
m = self.mav.recv_match(type='VFR_HUD', blocking=True)
513
initial_alt = m.alt
514
self.progress("Initial altitude %u\n" % initial_alt)
515
516
self.progress("Flying right circuit")
517
# do 4 turns
518
for i in range(0, 4):
519
# hard left
520
self.progress("Starting turn %u" % i)
521
self.set_rc(1, 1800)
522
try:
523
self.wait_heading(0 + (90*i), accuracy=20, timeout=60)
524
except Exception as e:
525
self.set_rc(1, 1500)
526
raise e
527
self.set_rc(1, 1500)
528
self.progress("Starting leg %u" % i)
529
self.wait_distance(100, accuracy=20)
530
self.progress("Circuit complete")
531
532
self.progress("Flying rudder left circuit")
533
# do 4 turns
534
for i in range(0, 4):
535
# hard left
536
self.progress("Starting turn %u" % i)
537
self.set_rc(4, 1900)
538
try:
539
self.wait_heading(360 - (90*i), accuracy=20, timeout=60)
540
except Exception as e:
541
self.set_rc(4, 1500)
542
raise e
543
self.set_rc(4, 1500)
544
self.progress("Starting leg %u" % i)
545
self.wait_distance(100, accuracy=20)
546
self.progress("Circuit complete")
547
548
m = self.mav.recv_match(type='VFR_HUD', blocking=True)
549
final_alt = m.alt
550
self.progress("Final altitude %u initial %u\n" %
551
(final_alt, initial_alt))
552
553
# back to FBWA
554
self.change_mode('FBWA')
555
556
if abs(final_alt - initial_alt) > 20:
557
raise NotAchievedException("Failed to maintain altitude")
558
559
return self.wait_level_flight()
560
561
def fly_mission(self, filename, mission_timeout=60.0, strict=True, quadplane=False):
562
"""Fly a mission from a file."""
563
self.progress("Flying mission %s" % filename)
564
num_wp = self.load_mission(filename, strict=strict)-1
565
self.fly_mission_waypoints(num_wp, mission_timeout=mission_timeout, quadplane=quadplane)
566
567
def fly_mission_waypoints(self, num_wp, mission_timeout=60.0, quadplane=False):
568
self.set_current_waypoint(0, check_afterwards=False)
569
self.context_push()
570
self.context_collect('STATUSTEXT')
571
self.change_mode('AUTO')
572
self.wait_waypoint(1, num_wp, max_dist=60, timeout=mission_timeout)
573
self.wait_groundspeed(0, 0.5, timeout=mission_timeout)
574
if quadplane:
575
self.wait_statustext("Throttle disarmed", timeout=200, check_context=True)
576
else:
577
self.wait_statustext("Auto disarmed", timeout=60, check_context=True)
578
self.context_pop()
579
self.progress("Mission OK")
580
581
def DO_REPOSITION(self):
582
'''Test mavlink DO_REPOSITION command'''
583
self.progress("Takeoff")
584
self.takeoff(alt=50)
585
self.set_rc(3, 1500)
586
self.progress("Entering guided and flying somewhere constant")
587
self.change_mode("GUIDED")
588
loc = self.mav.location()
589
self.location_offset_ne(loc, 500, 500)
590
591
new_alt = 100
592
self.run_cmd_int(
593
mavutil.mavlink.MAV_CMD_DO_REPOSITION,
594
p5=int(loc.lat * 1e7),
595
p6=int(loc.lng * 1e7),
596
p7=new_alt, # alt
597
frame=mavutil.mavlink.MAV_FRAME_GLOBAL_RELATIVE_ALT,
598
)
599
self.wait_altitude(new_alt-10, new_alt, timeout=30, relative=True)
600
601
self.install_terrain_handlers_context()
602
603
self.location_offset_ne(loc, 500, 500)
604
terrain_height_wanted = 150
605
self.run_cmd_int(
606
mavutil.mavlink.MAV_CMD_DO_REPOSITION,
607
0,
608
0,
609
0,
610
0,
611
int(loc.lat*1e7),
612
int(loc.lng*1e7),
613
terrain_height_wanted, # alt
614
frame=mavutil.mavlink.MAV_FRAME_GLOBAL_TERRAIN_ALT,
615
)
616
617
# move to specific terrain-relative altitude and hold for <n> seconds
618
tstart = self.get_sim_time_cached()
619
achieve_start = None
620
tr = None
621
while True:
622
if self.get_sim_time_cached() - tstart > 120:
623
raise NotAchievedException("Did not move to correct terrain alt")
624
625
m = self.mav.recv_match(type='TERRAIN_REPORT',
626
blocking=True,
627
timeout=1)
628
tr = m
629
terrain_height_achieved = m.current_height
630
self.progress("terrain_alt=%f want=%f" %
631
(terrain_height_achieved, terrain_height_wanted))
632
if m is None:
633
continue
634
if abs(terrain_height_wanted - terrain_height_achieved) > 5:
635
if achieve_start is not None:
636
self.progress("Achieve stop")
637
achieve_start = None
638
elif achieve_start is None:
639
self.progress("Achieve start")
640
achieve_start = self.get_sim_time_cached()
641
if achieve_start is not None:
642
if self.get_sim_time_cached() - achieve_start > 10:
643
break
644
m = self.mav.recv_match(type='GLOBAL_POSITION_INT',
645
blocking=True,
646
timeout=1)
647
self.progress("TR: %s" % tr)
648
self.progress("GPI: %s" % m)
649
min_delta = 4
650
delta = abs(m.relative_alt/1000.0 - tr.current_height)
651
if abs(delta < min_delta):
652
raise NotAchievedException("Expected altitude delta (want=%f got=%f)" %
653
(min_delta, delta))
654
655
self.fly_home_land_and_disarm(timeout=180)
656
657
def ExternalPositionEstimate(self):
658
'''Test mavlink EXTERNAL_POSITION_ESTIMATE command'''
659
if not hasattr(mavutil.mavlink, 'MAV_CMD_EXTERNAL_POSITION_ESTIMATE'):
660
raise OldpymavlinkException("pymavlink too old; upgrade pymavlink to get MAV_CMD_EXTERNAL_POSITION_ESTIMATE") # noqa
661
self.change_mode("TAKEOFF")
662
self.wait_ready_to_arm()
663
self.arm_vehicle()
664
self.wait_altitude(48, 52, relative=True)
665
666
loc = self.mav.location()
667
self.location_offset_ne(loc, 2000, 2000)
668
669
# setting external position fail while we have GPS lock
670
self.progress("set new position with GPS")
671
self.run_cmd_int(
672
mavutil.mavlink.MAV_CMD_EXTERNAL_POSITION_ESTIMATE,
673
p1=self.get_sim_time()-1, # transmit time
674
p2=0.5, # processing delay
675
p3=50, # accuracy
676
p5=int(loc.lat * 1e7),
677
p6=int(loc.lng * 1e7),
678
p7=float("NaN"), # alt
679
frame=mavutil.mavlink.MAV_FRAME_GLOBAL,
680
want_result=mavutil.mavlink.MAV_RESULT_FAILED,
681
)
682
683
self.progress("disable the GPS")
684
self.run_auxfunc(
685
65,
686
2,
687
want_result=mavutil.mavlink.MAV_RESULT_ACCEPTED
688
)
689
690
# fly for a bit to get into non-aiding state
691
self.progress("waiting 20 seconds")
692
tstart = self.get_sim_time()
693
while self.get_sim_time() < tstart + 20:
694
self.wait_heartbeat()
695
696
self.progress("getting base position")
697
gpi = self.mav.recv_match(
698
type='GLOBAL_POSITION_INT',
699
blocking=True,
700
timeout=5
701
)
702
loc = mavutil.location(gpi.lat*1e-7, gpi.lon*1e-7, 0, 0)
703
704
self.progress("set new position with no GPS")
705
self.run_cmd_int(
706
mavutil.mavlink.MAV_CMD_EXTERNAL_POSITION_ESTIMATE,
707
p1=self.get_sim_time()-1, # transmit time
708
p2=0.5, # processing delay
709
p3=50, # accuracy
710
p5=gpi.lat+1,
711
p6=gpi.lon+1,
712
p7=float("NaN"), # alt
713
frame=mavutil.mavlink.MAV_FRAME_GLOBAL,
714
want_result=mavutil.mavlink.MAV_RESULT_ACCEPTED
715
)
716
717
self.progress("waiting 3 seconds")
718
tstart = self.get_sim_time()
719
while self.get_sim_time() < tstart + 3:
720
self.wait_heartbeat()
721
722
gpi2 = self.mav.recv_match(
723
type='GLOBAL_POSITION_INT',
724
blocking=True,
725
timeout=5
726
)
727
loc2 = mavutil.location(gpi2.lat*1e-7, gpi2.lon*1e-7, 0, 0)
728
dist = self.get_distance(loc, loc2)
729
730
self.progress("dist is %.1f" % dist)
731
if dist > 200:
732
raise NotAchievedException("Position error dist=%.1f" % dist)
733
734
self.progress("re-enable the GPS")
735
self.run_auxfunc(
736
65,
737
0,
738
want_result=mavutil.mavlink.MAV_RESULT_ACCEPTED
739
)
740
741
self.progress("flying home")
742
self.fly_home_land_and_disarm()
743
744
def DeepStall(self):
745
'''Test DeepStall Landing'''
746
# self.fly_deepstall_absolute()
747
self.fly_deepstall_relative()
748
749
def fly_deepstall_absolute(self):
750
self.start_subtest("DeepStall Relative Absolute")
751
deepstall_elevator_pwm = 1661
752
self.set_parameters({
753
"LAND_TYPE": 1,
754
"LAND_DS_ELEV_PWM": deepstall_elevator_pwm,
755
"RTL_AUTOLAND": 1,
756
})
757
self.load_mission("plane-deepstall-mission.txt")
758
self.change_mode("AUTO")
759
self.wait_ready_to_arm()
760
self.arm_vehicle()
761
self.progress("Waiting for deepstall messages")
762
763
# note that the following two don't necessarily happen in this
764
# order, but at very high speedups we may miss the elevator
765
# PWM if we first look for the text (due to the get_sim_time()
766
# in wait_servo_channel_value)
767
768
self.context_collect('STATUSTEXT')
769
770
# assume elevator is on channel 2:
771
self.wait_servo_channel_value(2, deepstall_elevator_pwm, timeout=240)
772
773
self.wait_text("Deepstall: Entry: ", check_context=True)
774
775
self.disarm_wait(timeout=120)
776
777
self.progress("Flying home")
778
self.set_current_waypoint(0, check_afterwards=False)
779
self.takeoff(10)
780
self.set_parameter("LAND_TYPE", 0)
781
self.fly_home_land_and_disarm()
782
783
def fly_deepstall_relative(self):
784
self.start_subtest("DeepStall Relative")
785
deepstall_elevator_pwm = 1661
786
self.set_parameters({
787
"LAND_TYPE": 1,
788
"LAND_DS_ELEV_PWM": deepstall_elevator_pwm,
789
"RTL_AUTOLAND": 1,
790
})
791
self.load_mission("plane-deepstall-relative-mission.txt")
792
self.change_mode("AUTO")
793
self.wait_ready_to_arm()
794
self.arm_vehicle()
795
self.wait_current_waypoint(4)
796
797
# assume elevator is on channel 2:
798
self.wait_servo_channel_value(2, deepstall_elevator_pwm, timeout=240)
799
800
self.progress("Waiting for stage DEEPSTALL_STAGE_LAND")
801
self.assert_receive_message(
802
'DEEPSTALL',
803
condition='DEEPSTALL.stage==6',
804
timeout=240,
805
)
806
self.progress("Reached stage DEEPSTALL_STAGE_LAND")
807
808
self.disarm_wait(timeout=120)
809
self.set_current_waypoint(0, check_afterwards=False)
810
811
self.progress("Flying home")
812
self.set_current_waypoint(0, check_afterwards=False)
813
self.takeoff(100)
814
self.set_parameter("LAND_TYPE", 0)
815
self.fly_home_land_and_disarm(timeout=240)
816
817
def SmartBattery(self):
818
'''Test smart battery logging etc'''
819
self.set_parameters({
820
"BATT_MONITOR": 16, # Maxell battery monitor
821
})
822
823
# Must reboot sitl after setting montior type for SMBus parameters to be set due to dynamic group
824
self.reboot_sitl()
825
self.set_parameters({
826
"BATT_I2C_BUS": 2, # specified in SIM_I2C.cpp
827
"BATT_I2C_ADDR": 11, # specified in SIM_I2C.cpp
828
})
829
self.reboot_sitl()
830
831
self.wait_ready_to_arm()
832
m = self.assert_receive_message('BATTERY_STATUS', timeout=10)
833
if m.voltages_ext[0] == 65536:
834
raise NotAchievedException("Flag value rather than voltage")
835
if abs(m.voltages_ext[0] - 1000) > 300:
836
raise NotAchievedException("Did not get good ext voltage (got=%f)" %
837
(m.voltages_ext[0],))
838
self.arm_vehicle()
839
self.delay_sim_time(5)
840
self.disarm_vehicle()
841
if not self.current_onboard_log_contains_message("BCL2"):
842
raise NotAchievedException("Expected BCL2 message")
843
844
def context_push_do_change_speed(self):
845
# the following lines ensure we revert these parameter values
846
# - DO_CHANGE_AIRSPEED is a permanent vehicle change!
847
self.context_push()
848
self.set_parameters({
849
"AIRSPEED_CRUISE": self.get_parameter("AIRSPEED_CRUISE"),
850
"MIN_GROUNDSPEED": self.get_parameter("MIN_GROUNDSPEED"),
851
"TRIM_THROTTLE": self.get_parameter("TRIM_THROTTLE"),
852
})
853
854
def DO_CHANGE_SPEED(self):
855
'''Test DO_CHANGE_SPEED command/item'''
856
self.set_parameters({
857
"RTL_AUTOLAND": 1,
858
})
859
860
self.context_push_do_change_speed()
861
self.DO_CHANGE_SPEED_mavlink_long()
862
self.context_pop()
863
864
self.set_current_waypoint(1)
865
self.zero_throttle()
866
867
self.context_push_do_change_speed()
868
self.DO_CHANGE_SPEED_mavlink_int()
869
self.context_pop()
870
871
self.context_push_do_change_speed()
872
self.DO_CHANGE_SPEED_mission()
873
self.context_pop()
874
875
def DO_CHANGE_SPEED_mission(self):
876
'''test DO_CHANGE_SPEED as a mission item'''
877
self.start_subtest("DO_CHANGE_SPEED_mission")
878
self.load_mission("mission.txt")
879
self.set_current_waypoint(1)
880
881
self.progress("Takeoff")
882
self.set_rc(3, 1000)
883
self.takeoff(alt=10)
884
self.set_rc(3, 1500)
885
886
self.start_subtest("Check initial speed")
887
888
self.change_mode('AUTO')
889
890
checks = [
891
(1, self.get_parameter("AIRSPEED_CRUISE")),
892
(3, 10),
893
(5, 20),
894
(7, 15),
895
]
896
897
for (current_waypoint, want_airspeed) in checks:
898
self.wait_current_waypoint(current_waypoint, timeout=150)
899
self.wait_airspeed(want_airspeed-1, want_airspeed+1, minimum_duration=5, timeout=120)
900
901
self.fly_home_land_and_disarm()
902
903
def DO_CHANGE_SPEED_mavlink_int(self):
904
self.DO_CHANGE_SPEED_mavlink(self.run_cmd_int)
905
906
def DO_CHANGE_SPEED_mavlink_long(self):
907
self.DO_CHANGE_SPEED_mavlink(self.run_cmd)
908
909
def DO_CHANGE_SPEED_mavlink(self, run_cmd_method):
910
'''test DO_CHANGE_SPEED as a mavlink command'''
911
self.progress("Takeoff")
912
self.takeoff(alt=100, mode="TAKEOFF", timeout=120)
913
self.set_rc(3, 1500)
914
# ensure we know what the airspeed is:
915
self.progress("Entering guided and flying somewhere constant")
916
self.change_mode("GUIDED")
917
self.run_cmd_int(
918
mavutil.mavlink.MAV_CMD_DO_REPOSITION,
919
p5=12345, # lat* 1e7
920
p6=12345, # lon* 1e7
921
p7=100, # alt
922
frame=mavutil.mavlink.MAV_FRAME_GLOBAL_RELATIVE_ALT,
923
)
924
self.delay_sim_time(10)
925
self.progress("Ensuring initial speed is known and relatively constant")
926
initial_speed = 22.0
927
timeout = 15
928
self.wait_airspeed(initial_speed-1, initial_speed+1, minimum_duration=5, timeout=timeout)
929
930
self.start_subtest("Setting groundspeed")
931
for new_target_groundspeed in initial_speed + 5, initial_speed + 2:
932
run_cmd_method(
933
mavutil.mavlink.MAV_CMD_DO_CHANGE_SPEED,
934
p1=1, # groundspeed
935
p2=new_target_groundspeed,
936
p3=-1, # throttle / no change
937
p4=0, # absolute values
938
)
939
self.wait_groundspeed(new_target_groundspeed-2, new_target_groundspeed+2, timeout=80, minimum_duration=5)
940
self.progress("Adding some wind, ensuring groundspeed holds")
941
self.set_parameter("SIM_WIND_SPD", 5)
942
self.delay_sim_time(5)
943
self.wait_groundspeed(new_target_groundspeed-2, new_target_groundspeed+2, timeout=40, minimum_duration=5)
944
self.set_parameter("SIM_WIND_SPD", 0)
945
946
# clear target groundspeed
947
run_cmd_method(
948
mavutil.mavlink.MAV_CMD_DO_CHANGE_SPEED,
949
p1=1, # groundspeed
950
p2=0,
951
p3=-1, # throttle / no change
952
p4=0, # absolute values
953
)
954
955
self.start_subtest("Setting airspeed")
956
for new_target_airspeed in initial_speed - 5, initial_speed + 5:
957
run_cmd_method(
958
mavutil.mavlink.MAV_CMD_DO_CHANGE_SPEED,
959
p1=0, # airspeed
960
p2=new_target_airspeed,
961
p3=-1, # throttle / no change
962
p4=0, # absolute values
963
)
964
self.wait_airspeed(new_target_airspeed-2, new_target_airspeed+2, minimum_duration=5)
965
966
self.context_push()
967
self.progress("Adding some wind, hoping groundspeed increases/decreases")
968
self.set_parameters({
969
"SIM_WIND_SPD": 7,
970
"SIM_WIND_DIR": 270,
971
})
972
self.delay_sim_time(5)
973
timeout = 10
974
tstart = self.get_sim_time()
975
while True:
976
if self.get_sim_time_cached() - tstart > timeout:
977
raise NotAchievedException("Did not achieve groundspeed delta")
978
m = self.mav.recv_match(type='VFR_HUD', blocking=True)
979
delta = abs(m.airspeed - m.groundspeed)
980
want_delta = 5
981
self.progress("groundspeed and airspeed should be different (have=%f want=%f)" % (delta, want_delta))
982
if delta > want_delta:
983
break
984
self.context_pop()
985
986
# cancel minimum groundspeed:
987
run_cmd_method(
988
mavutil.mavlink.MAV_CMD_DO_CHANGE_SPEED,
989
p1=0, # groundspeed
990
p2=-2, # return to default
991
p3=0, # throttle / no change
992
p4=0, # absolute values
993
)
994
# cancel airspeed:
995
run_cmd_method(
996
mavutil.mavlink.MAV_CMD_DO_CHANGE_SPEED,
997
p1=1, # airspeed
998
p2=-2, # return to default
999
p3=0, # throttle / no change
1000
p4=0, # absolute values
1001
)
1002
1003
self.start_subtest("Setting throttle")
1004
self.set_parameter('ARSPD_USE', 0) # setting throttle only effective without airspeed
1005
for (set_throttle, expected_throttle) in (97, 79), (60, 51), (95, 77):
1006
run_cmd_method(
1007
mavutil.mavlink.MAV_CMD_DO_CHANGE_SPEED,
1008
p1=3, # throttle
1009
p2=0,
1010
p3=set_throttle, # throttle / no change
1011
p4=0, # absolute values
1012
)
1013
self.wait_message_field_values('VFR_HUD', {
1014
"throttle": expected_throttle,
1015
}, minimum_duration=5, epsilon=2)
1016
1017
self.fly_home_land_and_disarm(timeout=240)
1018
1019
def fly_home_land_and_disarm(self, timeout=120):
1020
filename = "flaps.txt"
1021
self.progress("Using %s to fly home" % filename)
1022
self.load_generic_mission(filename)
1023
self.change_mode("AUTO")
1024
# don't set current waypoint to 8 unless we're distant from it
1025
# or we arrive instantly and never see it as our current
1026
# waypoint:
1027
self.wait_distance_to_waypoint(8, 100, 10000000)
1028
self.set_current_waypoint(8)
1029
# TODO: reflect on file to find this magic waypoint number?
1030
# self.wait_waypoint(7, num_wp-1, timeout=500) # we
1031
# tend to miss the final waypoint by a fair bit, and
1032
# this is probably too noisy anyway?
1033
self.wait_disarmed(timeout=timeout)
1034
1035
def TestFlaps(self):
1036
"""Test flaps functionality."""
1037
filename = "flaps.txt"
1038
flaps_ch = 5
1039
flaps_ch_min = 1000
1040
flaps_ch_trim = 1500
1041
flaps_ch_max = 2000
1042
1043
servo_ch = 5
1044
servo_ch_min = 1200
1045
servo_ch_trim = 1300
1046
servo_ch_max = 1800
1047
1048
self.set_parameters({
1049
"SERVO%u_FUNCTION" % servo_ch: 3, # flapsauto
1050
"RC%u_OPTION" % flaps_ch: 208, # Flaps RCx_OPTION
1051
"LAND_FLAP_PERCNT": 50,
1052
"LOG_DISARMED": 1,
1053
"RTL_AUTOLAND": 1,
1054
1055
"RC%u_MIN" % flaps_ch: flaps_ch_min,
1056
"RC%u_MAX" % flaps_ch: flaps_ch_max,
1057
"RC%u_TRIM" % flaps_ch: flaps_ch_trim,
1058
1059
"SERVO%u_MIN" % servo_ch: servo_ch_min,
1060
"SERVO%u_MAX" % servo_ch: servo_ch_max,
1061
"SERVO%u_TRIM" % servo_ch: servo_ch_trim,
1062
})
1063
1064
self.progress("check flaps are not deployed")
1065
self.set_rc(flaps_ch, flaps_ch_min)
1066
self.wait_servo_channel_value(servo_ch, servo_ch_min, timeout=3)
1067
self.progress("deploy the flaps")
1068
self.set_rc(flaps_ch, flaps_ch_max)
1069
tstart = self.get_sim_time()
1070
self.wait_servo_channel_value(servo_ch, servo_ch_max)
1071
tstop = self.get_sim_time_cached()
1072
delta_time = tstop - tstart
1073
delta_time_min = 0.5
1074
delta_time_max = 1.5
1075
if delta_time < delta_time_min or delta_time > delta_time_max:
1076
raise NotAchievedException((
1077
"Flaps Slew not working (%f seconds)" % (delta_time,)))
1078
self.progress("undeploy flaps")
1079
self.set_rc(flaps_ch, flaps_ch_min)
1080
self.wait_servo_channel_value(servo_ch, servo_ch_min)
1081
1082
self.progress("Flying mission %s" % filename)
1083
self.load_mission(filename)
1084
self.change_mode('AUTO')
1085
self.wait_ready_to_arm()
1086
self.arm_vehicle()
1087
# flaps should deploy for landing (RC input value used for position?!)
1088
self.wait_servo_channel_value(servo_ch, flaps_ch_trim, timeout=300)
1089
# flaps should undeploy at the end
1090
self.wait_servo_channel_value(servo_ch, servo_ch_min, timeout=30)
1091
1092
self.progress("Flaps OK")
1093
1094
def TestRCRelay(self):
1095
'''Test Relay RC Channel Option'''
1096
self.set_parameters({
1097
"RELAY1_FUNCTION": 1, # Enable relay as a standard relay pin
1098
"RC12_OPTION": 28 # Relay On/Off
1099
})
1100
self.set_rc(12, 1000)
1101
self.reboot_sitl() # needed for RC12_OPTION and RELAY1_FUNCTION to take effect
1102
1103
off = self.get_parameter("SIM_PIN_MASK")
1104
if off:
1105
raise PreconditionFailedException("SIM_MASK_PIN off")
1106
1107
# allow time for the RC library to register initial value:
1108
self.delay_sim_time(1)
1109
1110
self.set_rc(12, 2000)
1111
self.wait_heartbeat()
1112
self.wait_heartbeat()
1113
1114
on = self.get_parameter("SIM_PIN_MASK")
1115
if not on:
1116
raise NotAchievedException("SIM_PIN_MASK doesn't reflect ON")
1117
self.set_rc(12, 1000)
1118
self.wait_heartbeat()
1119
self.wait_heartbeat()
1120
off = self.get_parameter("SIM_PIN_MASK")
1121
if off:
1122
raise NotAchievedException("SIM_PIN_MASK doesn't reflect OFF")
1123
1124
def TestRCCamera(self):
1125
'''Test RC Option - Camera Trigger'''
1126
self.set_parameter("RC12_OPTION", 9) # CameraTrigger
1127
self.set_parameter("CAM1_TYPE", 1) # Camera with servo trigger
1128
self.reboot_sitl() # needed for RC12_OPTION to take effect
1129
1130
x = self.mav.messages.get("CAMERA_FEEDBACK", None)
1131
if x is not None:
1132
raise PreconditionFailedException("Receiving CAMERA_FEEDBACK?!")
1133
self.set_rc(12, 2000)
1134
tstart = self.get_sim_time()
1135
while self.get_sim_time_cached() - tstart < 10:
1136
x = self.mav.messages.get("CAMERA_FEEDBACK", None)
1137
if x is not None:
1138
break
1139
self.wait_heartbeat()
1140
self.set_rc(12, 1000)
1141
if x is None:
1142
raise NotAchievedException("No CAMERA_FEEDBACK message received")
1143
1144
self.wait_ready_to_arm()
1145
1146
original_alt = self.get_altitude()
1147
1148
takeoff_alt = 30
1149
self.takeoff(takeoff_alt)
1150
self.set_rc(12, 2000)
1151
self.delay_sim_time(1)
1152
self.set_rc(12, 1000)
1153
x = self.mav.messages.get("CAMERA_FEEDBACK", None)
1154
if abs(x.alt_rel - takeoff_alt) > 10:
1155
raise NotAchievedException("Bad relalt (want=%f vs got=%f)" % (takeoff_alt, x.alt_rel))
1156
if abs(x.alt_msl - (original_alt+30)) > 10:
1157
raise NotAchievedException("Bad absalt (want=%f vs got=%f)" % (original_alt+30, x.alt_msl))
1158
self.fly_home_land_and_disarm()
1159
1160
def ThrottleFailsafe(self):
1161
'''Fly throttle failsafe'''
1162
self.change_mode('MANUAL')
1163
m = self.mav.recv_match(type='SYS_STATUS', blocking=True)
1164
receiver_bit = mavutil.mavlink.MAV_SYS_STATUS_SENSOR_RC_RECEIVER
1165
self.progress("Testing receiver enabled")
1166
if (not (m.onboard_control_sensors_enabled & receiver_bit)):
1167
raise PreconditionFailedException()
1168
self.progress("Testing receiver present")
1169
if (not (m.onboard_control_sensors_present & receiver_bit)):
1170
raise PreconditionFailedException()
1171
self.progress("Testing receiver health")
1172
if (not (m.onboard_control_sensors_health & receiver_bit)):
1173
raise PreconditionFailedException()
1174
1175
self.progress("Ensure we know original throttle value")
1176
self.wait_rc_channel_value(3, 1000)
1177
1178
self.set_parameter("THR_FS_VALUE", 960)
1179
self.progress("Failing receiver (throttle-to-950)")
1180
self.context_collect("HEARTBEAT")
1181
self.set_parameter("SIM_RC_FAIL", 2) # throttle-to-950
1182
self.wait_mode('RTL') # long failsafe
1183
if (self.get_mode_from_mode_mapping("CIRCLE") not in
1184
[x.custom_mode for x in self.context_stop_collecting("HEARTBEAT")]):
1185
raise NotAchievedException("Did not go via circle mode")
1186
self.progress("Ensure we've had our throttle squashed to 950")
1187
self.wait_rc_channel_value(3, 950)
1188
self.do_timesync_roundtrip()
1189
m = self.assert_receive_message('SYS_STATUS')
1190
self.progress("Got (%s)" % str(m))
1191
self.progress("Testing receiver enabled")
1192
if (not (m.onboard_control_sensors_enabled & receiver_bit)):
1193
raise NotAchievedException("Receiver not enabled")
1194
self.progress("Testing receiver present")
1195
if (not (m.onboard_control_sensors_present & receiver_bit)):
1196
raise NotAchievedException("Receiver not present")
1197
# skip this until RC is fixed
1198
# self.progress("Testing receiver health")
1199
# if (m.onboard_control_sensors_health & receiver_bit):
1200
# raise NotAchievedException("Sensor healthy when it shouldn't be")
1201
self.set_parameter("SIM_RC_FAIL", 0)
1202
# have to allow time for RC to be fetched from SITL
1203
self.delay_sim_time(0.5)
1204
self.do_timesync_roundtrip()
1205
m = self.assert_receive_message('SYS_STATUS')
1206
self.progress("Testing receiver enabled")
1207
if (not (m.onboard_control_sensors_enabled & receiver_bit)):
1208
raise NotAchievedException("Receiver not enabled")
1209
self.progress("Testing receiver present")
1210
if (not (m.onboard_control_sensors_present & receiver_bit)):
1211
raise NotAchievedException("Receiver not present")
1212
self.progress("Testing receiver health")
1213
if (not (m.onboard_control_sensors_health & receiver_bit)):
1214
raise NotAchievedException("Receiver not healthy2")
1215
self.change_mode('MANUAL')
1216
1217
self.progress("Failing receiver (no-pulses)")
1218
self.context_collect("HEARTBEAT")
1219
self.set_parameter("SIM_RC_FAIL", 1) # no-pulses
1220
self.wait_mode('RTL') # long failsafe
1221
if (self.get_mode_from_mode_mapping("CIRCLE") not in
1222
[x.custom_mode for x in self.context_stop_collecting("HEARTBEAT")]):
1223
raise NotAchievedException("Did not go via circle mode")
1224
self.do_timesync_roundtrip()
1225
m = self.assert_receive_message('SYS_STATUS')
1226
self.progress("Got (%s)" % str(m))
1227
self.progress("Testing receiver enabled")
1228
if (not (m.onboard_control_sensors_enabled & receiver_bit)):
1229
raise NotAchievedException("Receiver not enabled")
1230
self.progress("Testing receiver present")
1231
if (not (m.onboard_control_sensors_present & receiver_bit)):
1232
raise NotAchievedException("Receiver not present")
1233
self.progress("Testing receiver health")
1234
if (m.onboard_control_sensors_health & receiver_bit):
1235
raise NotAchievedException("Sensor healthy when it shouldn't be")
1236
self.progress("Making RC work again")
1237
self.set_parameter("SIM_RC_FAIL", 0)
1238
# have to allow time for RC to be fetched from SITL
1239
self.progress("Giving receiver time to recover")
1240
self.delay_sim_time(0.5)
1241
self.do_timesync_roundtrip()
1242
m = self.assert_receive_message('SYS_STATUS')
1243
self.progress("Testing receiver enabled")
1244
if (not (m.onboard_control_sensors_enabled & receiver_bit)):
1245
raise NotAchievedException("Receiver not enabled")
1246
self.progress("Testing receiver present")
1247
if (not (m.onboard_control_sensors_present & receiver_bit)):
1248
raise NotAchievedException("Receiver not present")
1249
self.progress("Testing receiver health")
1250
if (not (m.onboard_control_sensors_health & receiver_bit)):
1251
raise NotAchievedException("Receiver not healthy")
1252
self.change_mode('MANUAL')
1253
1254
self.progress("Ensure long failsafe can trigger when short failsafe disabled")
1255
self.context_push()
1256
self.context_collect("STATUSTEXT")
1257
self.set_parameters({
1258
"FS_SHORT_ACTN": 3, # 3 means disabled
1259
"SIM_RC_FAIL": 1,
1260
})
1261
self.wait_statustext("Long failsafe on", check_context=True)
1262
self.wait_mode("RTL")
1263
# self.context_clear_collection("STATUSTEXT")
1264
self.set_parameter("SIM_RC_FAIL", 0)
1265
self.wait_text("Long Failsafe Cleared", check_context=True)
1266
self.change_mode("MANUAL")
1267
1268
self.progress("Trying again with THR_FS_VALUE")
1269
self.set_parameters({
1270
"THR_FS_VALUE": 960,
1271
"SIM_RC_FAIL": 2,
1272
})
1273
self.wait_statustext("Long Failsafe on", check_context=True)
1274
self.wait_mode("RTL")
1275
self.context_pop()
1276
1277
self.start_subtest("Not use RC throttle input when THR_FAILSAFE==2")
1278
self.takeoff(100)
1279
self.set_rc(3, 1800)
1280
self.set_rc(1, 2000)
1281
self.wait_attitude(desroll=45, timeout=1)
1282
self.context_push()
1283
self.set_parameters({
1284
"THR_FAILSAFE": 2,
1285
"SIM_RC_FAIL": 1, # no pulses
1286
})
1287
self.delay_sim_time(1)
1288
self.wait_attitude(desroll=0, timeout=5)
1289
self.assert_servo_channel_value(3, self.get_parameter("RC3_MIN"))
1290
self.set_parameters({
1291
"SIM_RC_FAIL": 0, # fix receiver
1292
})
1293
self.zero_throttle()
1294
self.disarm_vehicle(force=True)
1295
self.context_pop()
1296
self.reboot_sitl()
1297
1298
def ThrottleFailsafeFence(self):
1299
'''Fly fence survives throttle failsafe'''
1300
fence_bit = mavutil.mavlink.MAV_SYS_STATUS_GEOFENCE
1301
1302
self.progress("Checking fence is not present before being configured")
1303
m = self.mav.recv_match(type='SYS_STATUS', blocking=True)
1304
self.progress("Got (%s)" % str(m))
1305
if (m.onboard_control_sensors_enabled & fence_bit):
1306
raise NotAchievedException("Fence enabled before being configured")
1307
1308
self.change_mode('MANUAL')
1309
self.wait_ready_to_arm()
1310
1311
self.load_fence("CMAC-fence.txt")
1312
1313
self.set_parameter("RC7_OPTION", 11) # AC_Fence uses Aux switch functionality
1314
self.set_parameter("FENCE_ACTION", 4) # Fence action Brake
1315
self.set_rc_from_map({
1316
3: 1000,
1317
7: 2000,
1318
}) # Turn fence on with aux function
1319
1320
m = self.mav.recv_match(type='FENCE_STATUS', blocking=True, timeout=2)
1321
self.progress("Got (%s)" % str(m))
1322
if m is None:
1323
raise NotAchievedException("Got FENCE_STATUS unexpectedly")
1324
1325
self.progress("Checking fence is initially OK")
1326
self.wait_sensor_state(mavutil.mavlink.MAV_SYS_STATUS_GEOFENCE,
1327
present=True,
1328
enabled=True,
1329
healthy=True,
1330
verbose=True,
1331
timeout=30)
1332
1333
self.set_parameter("THR_FS_VALUE", 960)
1334
self.progress("Failing receiver (throttle-to-950)")
1335
self.set_parameter("SIM_RC_FAIL", 2) # throttle-to-950
1336
self.wait_mode("CIRCLE")
1337
self.delay_sim_time(1) # give
1338
self.do_timesync_roundtrip()
1339
1340
self.progress("Checking fence is OK after receiver failure (bind-values)")
1341
fence_bit = mavutil.mavlink.MAV_SYS_STATUS_GEOFENCE
1342
m = self.assert_receive_message('SYS_STATUS')
1343
if (not (m.onboard_control_sensors_enabled & fence_bit)):
1344
raise NotAchievedException("Fence not enabled after RC fail")
1345
self.do_fence_disable() # Ensure the fence is disabled after test
1346
1347
def GCSFailsafe(self):
1348
'''Ensure Long-Failsafe works on GCS loss'''
1349
self.start_subtest("Test Failsafe: RTL")
1350
self.load_sample_mission()
1351
self.set_parameters({
1352
"FS_GCS_ENABL": 1,
1353
"FS_LONG_ACTN": 1,
1354
"RTL_AUTOLAND": 1,
1355
"SYSID_MYGCS": self.mav.source_system,
1356
})
1357
self.takeoff()
1358
self.change_mode('LOITER')
1359
self.progress("Disconnecting GCS")
1360
self.set_heartbeat_rate(0)
1361
self.wait_mode("RTL", timeout=10)
1362
self.set_heartbeat_rate(self.speedup)
1363
self.end_subtest("Completed RTL Failsafe test")
1364
1365
self.start_subtest("Test Failsafe: FBWA Glide")
1366
self.set_parameters({
1367
"FS_LONG_ACTN": 2,
1368
})
1369
self.change_mode('AUTO')
1370
self.progress("Disconnecting GCS")
1371
self.set_heartbeat_rate(0)
1372
self.wait_mode("FBWA", timeout=10)
1373
self.set_heartbeat_rate(self.speedup)
1374
self.end_subtest("Completed FBWA Failsafe test")
1375
1376
self.start_subtest("Test Failsafe: Deploy Parachute")
1377
self.load_mission("plane-parachute-mission.txt")
1378
self.set_current_waypoint(1)
1379
self.set_parameters({
1380
"CHUTE_ENABLED": 1,
1381
"CHUTE_TYPE": 10,
1382
"SERVO9_FUNCTION": 27,
1383
"SIM_PARA_ENABLE": 1,
1384
"SIM_PARA_PIN": 9,
1385
"FS_LONG_ACTN": 3,
1386
})
1387
self.change_mode("AUTO")
1388
self.progress("Disconnecting GCS")
1389
self.set_heartbeat_rate(0)
1390
self.wait_statustext("BANG", timeout=60)
1391
self.set_heartbeat_rate(self.speedup)
1392
self.disarm_vehicle(force=True)
1393
self.reboot_sitl()
1394
self.end_subtest("Completed Parachute Failsafe test")
1395
1396
def TestGripperMission(self):
1397
'''Test Gripper mission items'''
1398
self.set_parameter("RTL_AUTOLAND", 1)
1399
self.load_mission("plane-gripper-mission.txt")
1400
self.set_current_waypoint(1)
1401
self.change_mode('AUTO')
1402
self.wait_ready_to_arm()
1403
self.arm_vehicle()
1404
self.wait_statustext("Gripper Grabbed", timeout=60)
1405
self.wait_statustext("Gripper Released", timeout=60)
1406
self.wait_statustext("Auto disarmed", timeout=60)
1407
1408
def assert_fence_sys_status(self, present, enabled, health):
1409
self.delay_sim_time(1)
1410
self.do_timesync_roundtrip()
1411
m = self.assert_receive_message('SYS_STATUS', timeout=1)
1412
tests = [
1413
("present", present, m.onboard_control_sensors_present),
1414
("enabled", enabled, m.onboard_control_sensors_enabled),
1415
("health", health, m.onboard_control_sensors_health),
1416
]
1417
bit = mavutil.mavlink.MAV_SYS_STATUS_GEOFENCE
1418
for test in tests:
1419
(name, want, field) = test
1420
got = (field & bit) != 0
1421
if want != got:
1422
raise NotAchievedException("fence status incorrect; %s want=%u got=%u" %
1423
(name, want, got))
1424
1425
def wait_circling_point_with_radius(self, loc, want_radius, epsilon=5.0, min_circle_time=5, timeout=120):
1426
on_radius_start_heading = None
1427
average_radius = 0.0
1428
circle_time_start = 0
1429
done_time = False
1430
done_angle = False
1431
tstart = self.get_sim_time()
1432
while True:
1433
if self.get_sim_time() - tstart > timeout:
1434
raise AutoTestTimeoutException("Did not get onto circle")
1435
here = self.mav.location()
1436
got_radius = self.get_distance(loc, here)
1437
average_radius = 0.95*average_radius + 0.05*got_radius
1438
on_radius = abs(got_radius - want_radius) < epsilon
1439
m = self.mav.recv_match(type='VFR_HUD', blocking=True)
1440
heading = m.heading
1441
on_string = "off"
1442
got_angle = ""
1443
if on_radius_start_heading is not None:
1444
got_angle = "%0.2f" % abs(on_radius_start_heading - heading) # FIXME
1445
on_string = "on"
1446
1447
want_angle = 180 # we don't actually get this (angle-substraction issue. But we get enough...
1448
self.progress("wait-circling: got-r=%0.2f want-r=%f avg-r=%f %s want-a=%0.1f got-a=%s" %
1449
(got_radius, want_radius, average_radius, on_string, want_angle, got_angle))
1450
if on_radius:
1451
if on_radius_start_heading is None:
1452
on_radius_start_heading = heading
1453
average_radius = got_radius
1454
circle_time_start = self.get_sim_time()
1455
continue
1456
if abs(on_radius_start_heading - heading) > want_angle: # FIXME
1457
done_angle = True
1458
if self.get_sim_time() - circle_time_start > min_circle_time:
1459
done_time = True
1460
if done_time and done_angle:
1461
return
1462
continue
1463
if on_radius_start_heading is not None:
1464
average_radius = 0.0
1465
on_radius_start_heading = None
1466
circle_time_start = 0
1467
1468
def MODE_SWITCH_RESET(self):
1469
'''test the MODE_SWITCH_RESET auxiliary function'''
1470
self.set_parameters({
1471
"RC9_OPTION": 96,
1472
})
1473
1474
self.progress("Using RC to change modes")
1475
self.set_rc(8, 1500)
1476
self.wait_mode('FBWA')
1477
1478
self.progress("Killing RC to engage RC failsafe")
1479
self.set_parameter('SIM_RC_FAIL', 1)
1480
self.wait_mode('RTL')
1481
1482
self.progress("Reinstating RC")
1483
self.set_parameter('SIM_RC_FAIL', 0)
1484
1485
self.progress("Ensuring we don't automatically revert mode")
1486
self.delay_sim_time(2)
1487
self.assert_mode_is('RTL')
1488
1489
self.progress("Ensuring MODE_SWITCH_RESET switch resets to pre-failsafe mode")
1490
self.set_rc(9, 2000)
1491
self.wait_mode('FBWA')
1492
1493
def FenceStatic(self):
1494
'''Test Basic Fence Functionality'''
1495
self.progress("Checking for bizarre healthy-when-not-present-or-enabled")
1496
self.set_parameter("FENCE_TYPE", 4) # Start by only setting polygon fences, otherwise fence will report present
1497
self.assert_fence_sys_status(False, False, True)
1498
self.load_fence("CMAC-fence.txt")
1499
m = self.mav.recv_match(type='FENCE_STATUS', blocking=True, timeout=2)
1500
if m is not None:
1501
raise NotAchievedException("Got FENCE_STATUS unexpectedly")
1502
self.set_parameter("FENCE_ACTION", 0) # report only
1503
self.assert_fence_sys_status(True, False, True)
1504
self.set_parameter("FENCE_ACTION", 1) # RTL
1505
self.assert_fence_sys_status(True, False, True)
1506
self.do_fence_enable()
1507
self.assert_fence_sys_status(True, True, True)
1508
m = self.assert_receive_message('FENCE_STATUS', timeout=2)
1509
if m.breach_status:
1510
raise NotAchievedException("Breached fence unexpectedly (%u)" %
1511
(m.breach_status))
1512
self.do_fence_disable()
1513
self.assert_fence_sys_status(True, False, True)
1514
self.set_parameter("FENCE_ACTION", 1)
1515
self.assert_fence_sys_status(True, False, True)
1516
self.set_parameter("FENCE_ACTION", 0)
1517
self.assert_fence_sys_status(True, False, True)
1518
self.clear_fence()
1519
if self.get_parameter("FENCE_TOTAL") != 0:
1520
raise NotAchievedException("Expected zero points remaining")
1521
self.assert_fence_sys_status(False, False, True)
1522
self.progress("Trying to enable fence with no points")
1523
self.do_fence_enable(want_result=mavutil.mavlink.MAV_RESULT_FAILED)
1524
1525
# test a rather unfortunate behaviour:
1526
self.progress("Killing a live fence with fence-clear")
1527
self.load_fence("CMAC-fence.txt")
1528
self.set_parameter("FENCE_ACTION", 1) # AC_FENCE_ACTION_RTL_AND_LAND == 1. mavutil.mavlink.FENCE_ACTION_RTL == 4
1529
self.do_fence_enable()
1530
self.assert_fence_sys_status(True, True, True)
1531
self.clear_fence()
1532
self.wait_sensor_state(mavutil.mavlink.MAV_SYS_STATUS_GEOFENCE, False, False, True)
1533
if self.get_parameter("FENCE_TOTAL") != 0:
1534
raise NotAchievedException("Expected zero points remaining")
1535
self.assert_fence_sys_status(False, False, True)
1536
self.do_fence_disable()
1537
1538
# ensure that a fence is present if it is tin can, min alt or max alt
1539
self.progress("Test other fence types (tin-can, min alt, max alt")
1540
self.set_parameter("FENCE_TYPE", 1) # max alt
1541
self.assert_fence_sys_status(True, False, True)
1542
self.set_parameter("FENCE_TYPE", 8) # min alt
1543
self.assert_fence_sys_status(True, False, True)
1544
self.set_parameter("FENCE_TYPE", 2) # tin can
1545
self.assert_fence_sys_status(True, False, True)
1546
1547
# Test cannot arm if outside of fence and fence is enabled
1548
self.progress("Test Arming while vehicle below FENCE_ALT_MIN")
1549
default_fence_alt_min = self.get_parameter("FENCE_ALT_MIN")
1550
self.set_parameter("FENCE_ALT_MIN", 50)
1551
self.set_parameter("FENCE_TYPE", 8) # Enables minimum altitude breaches
1552
self.do_fence_enable()
1553
self.delay_sim_time(2) # Allow breach to propagate
1554
self.assert_fence_enabled()
1555
1556
self.try_arm(False, "Vehicle breaching Min Alt fence")
1557
self.do_fence_disable()
1558
self.set_parameter("FENCE_ALT_MIN", default_fence_alt_min)
1559
1560
# Test arming outside inclusion zone
1561
self.progress("Test arming while Vehicle breaching of inclusion zone")
1562
self.set_parameter("FENCE_TYPE", 4) # Enables polygon fence types
1563
self.upload_fences_from_locations([(
1564
mavutil.mavlink.MAV_CMD_NAV_FENCE_POLYGON_VERTEX_INCLUSION, [
1565
mavutil.location(1.000, 1.000, 0, 0),
1566
mavutil.location(1.000, 1.001, 0, 0),
1567
mavutil.location(1.001, 1.001, 0, 0),
1568
mavutil.location(1.001, 1.000, 0, 0)
1569
]
1570
)])
1571
self.delay_sim_time(10) # let fence check run so it loads-from-eeprom
1572
self.do_fence_enable()
1573
self.assert_fence_enabled()
1574
self.delay_sim_time(2) # Allow breach to propagate
1575
self.try_arm(False, "Vehicle breaching Polygon fence")
1576
self.do_fence_disable()
1577
self.clear_fence()
1578
1579
self.progress("Test arming while vehicle inside exclusion zone")
1580
self.set_parameter("FENCE_TYPE", 4) # Enables polygon fence types
1581
home_loc = self.mav.location()
1582
locs = [
1583
mavutil.location(home_loc.lat - 0.001, home_loc.lng - 0.001, 0, 0),
1584
mavutil.location(home_loc.lat - 0.001, home_loc.lng + 0.001, 0, 0),
1585
mavutil.location(home_loc.lat + 0.001, home_loc.lng + 0.001, 0, 0),
1586
mavutil.location(home_loc.lat + 0.001, home_loc.lng - 0.001, 0, 0),
1587
]
1588
self.upload_fences_from_locations([
1589
(mavutil.mavlink.MAV_CMD_NAV_FENCE_POLYGON_VERTEX_EXCLUSION, locs),
1590
])
1591
self.delay_sim_time(10) # let fence check run so it loads-from-eeprom
1592
self.do_fence_enable()
1593
self.assert_fence_enabled()
1594
self.delay_sim_time(2) # Allow breach to propagate
1595
self.try_arm(False, "Vehicle breaching Polygon fence")
1596
self.do_fence_disable()
1597
1598
def test_fence_breach_circle_at(self, loc, disable_on_breach=False):
1599
self.load_fence("CMAC-fence.txt")
1600
want_radius = 100
1601
# when ArduPlane is fixed, remove this fudge factor
1602
REALLY_BAD_FUDGE_FACTOR = 1.16
1603
expected_radius = REALLY_BAD_FUDGE_FACTOR * want_radius
1604
self.set_parameters({
1605
"RTL_RADIUS": want_radius,
1606
"NAVL1_LIM_BANK": 60,
1607
"FENCE_ACTION": 1, # AC_FENCE_ACTION_RTL_AND_LAND == 1. mavutil.mavlink.FENCE_ACTION_RTL == 4
1608
})
1609
1610
self.wait_ready_to_arm() # need an origin to load fence
1611
1612
self.do_fence_enable()
1613
self.assert_fence_sys_status(True, True, True)
1614
1615
self.takeoff(alt=45, alt_max=300)
1616
1617
tstart = self.get_sim_time()
1618
while True:
1619
if self.get_sim_time() - tstart > 30:
1620
raise NotAchievedException("Did not breach fence")
1621
m = self.assert_receive_message('FENCE_STATUS', timeout=2)
1622
if m.breach_status == 0:
1623
continue
1624
1625
# we've breached; check our state;
1626
if m.breach_type != mavutil.mavlink.FENCE_BREACH_BOUNDARY:
1627
raise NotAchievedException("Unexpected breach type %u" %
1628
(m.breach_type,))
1629
if m.breach_count == 0:
1630
raise NotAchievedException("Unexpected breach count %u" %
1631
(m.breach_count,))
1632
self.assert_fence_sys_status(True, True, False)
1633
break
1634
1635
self.wait_circling_point_with_radius(loc, expected_radius)
1636
self.do_fence_disable()
1637
self.disarm_vehicle(force=True)
1638
self.reboot_sitl()
1639
1640
def FenceRTL(self):
1641
'''Test Fence RTL'''
1642
self.progress("Testing FENCE_ACTION_RTL no rally point")
1643
# have to disable the fence once we've breached or we breach
1644
# it as part of the loiter-at-home!
1645
self.test_fence_breach_circle_at(self.home_position_as_mav_location())
1646
1647
def FenceRTLRally(self):
1648
'''Test Fence RTL Rally'''
1649
self.progress("Testing FENCE_ACTION_RTL with rally point")
1650
1651
self.wait_ready_to_arm()
1652
loc = self.home_relative_loc_ne(50, -50)
1653
self.upload_rally_points_from_locations([loc])
1654
self.test_fence_breach_circle_at(loc)
1655
1656
def FenceRetRally(self):
1657
""" Tests the FENCE_RET_RALLY flag, either returning to fence return point,
1658
or rally point """
1659
target_system = 1
1660
target_component = 1
1661
self.progress("Testing FENCE_ACTION_RTL with fence rally point")
1662
1663
self.wait_ready_to_arm()
1664
1665
# Grab a location for fence return point, and upload it.
1666
fence_loc = self.home_position_as_mav_location()
1667
self.location_offset_ne(fence_loc, 50, 50)
1668
fence_return_mission_items = [
1669
self.mav.mav.mission_item_int_encode(
1670
target_system,
1671
target_component,
1672
0, # seq
1673
mavutil.mavlink.MAV_FRAME_GLOBAL_INT,
1674
mavutil.mavlink.MAV_CMD_NAV_FENCE_RETURN_POINT,
1675
0, # current
1676
0, # autocontinue
1677
0, # p1
1678
0, # p2
1679
0, # p3
1680
0, # p4
1681
int(fence_loc.lat * 1e7), # latitude
1682
int(fence_loc.lng * 1e7), # longitude
1683
0, # altitude
1684
mavutil.mavlink.MAV_MISSION_TYPE_FENCE
1685
)
1686
]
1687
self.upload_using_mission_protocol(mavutil.mavlink.MAV_MISSION_TYPE_FENCE,
1688
fence_return_mission_items)
1689
self.delay_sim_time(1)
1690
1691
# Grab a location for rally point, and upload it.
1692
rally_loc = self.home_relative_loc_ne(-50, 50)
1693
self.upload_rally_points_from_locations([rally_loc])
1694
1695
return_radius = 100
1696
return_alt = 80
1697
self.set_parameters({
1698
"RTL_RADIUS": return_radius,
1699
"FENCE_ACTION": 6, # Set Fence Action to Guided
1700
"FENCE_TYPE": 8, # Only use fence floor
1701
"FENCE_RET_ALT": return_alt,
1702
})
1703
self.do_fence_enable()
1704
self.assert_fence_enabled()
1705
1706
self.takeoff(alt=50, alt_max=300)
1707
# Trigger fence breach, fly to rally location
1708
self.set_parameters({
1709
"FENCE_RET_RALLY": 1,
1710
"FENCE_ALT_MIN": 60,
1711
})
1712
self.wait_circling_point_with_radius(rally_loc, return_radius)
1713
self.set_parameter("FENCE_ALT_MIN", 0) # Clear fence breach
1714
1715
# 10 second fence min retrigger time
1716
self.delay_sim_time(15)
1717
1718
# Fly up before re-triggering fence breach. Fly to fence return point
1719
self.change_altitude(30, relative=True)
1720
self.set_parameters({
1721
"FENCE_RET_RALLY": 0,
1722
"FENCE_ALT_MIN": 60,
1723
})
1724
self.wait_altitude(altitude_min=return_alt-3,
1725
altitude_max=return_alt+3,
1726
relative=True)
1727
self.wait_circling_point_with_radius(fence_loc, return_radius)
1728
self.do_fence_disable() # Disable fence so we can land
1729
self.fly_home_land_and_disarm() # Pack it up, we're going home.
1730
1731
def TerrainRally(self):
1732
""" Tests terrain follow with a rally point """
1733
self.context_push()
1734
self.install_terrain_handlers_context()
1735
1736
def terrain_following_above_80m(mav, m):
1737
if m.get_type() == 'TERRAIN_REPORT':
1738
if m.current_height < 50:
1739
raise NotAchievedException(
1740
"TERRAIN_REPORT.current_height below 50m %fm" % m.current_height)
1741
if m.get_type() == 'VFR_HUD':
1742
if m.groundspeed < 2:
1743
raise NotAchievedException("hit ground")
1744
1745
def terrain_wait_path(loc1, loc2, steps):
1746
'''wait till we have terrain for N steps from loc1 to loc2'''
1747
tstart = self.get_sim_time_cached()
1748
self.progress("Waiting for terrain data")
1749
while True:
1750
now = self.get_sim_time_cached()
1751
if now - tstart > 60:
1752
raise NotAchievedException("Did not get correct required terrain")
1753
for i in range(steps):
1754
lat = loc1.lat + i * (loc2.lat-loc1.lat)/steps
1755
lon = loc1.lng + i * (loc2.lng-loc1.lng)/steps
1756
self.mav.mav.terrain_check_send(int(lat*1.0e7), int(lon*1.0e7))
1757
1758
report = self.assert_receive_message('TERRAIN_REPORT', timeout=60)
1759
self.progress("Terrain pending=%u" % report.pending)
1760
if report.pending == 0:
1761
break
1762
self.progress("Got required terrain")
1763
1764
self.wait_ready_to_arm()
1765
homeloc = self.mav.location()
1766
1767
guided_loc = mavutil.location(-35.39723762, 149.07284612, homeloc.alt+99.0, 0)
1768
rally_loc = mavutil.location(-35.3654952000, 149.1558698000, homeloc.alt+100, 0)
1769
1770
terrain_wait_path(homeloc, rally_loc, 10)
1771
1772
# set a rally point to the west of home
1773
self.upload_rally_points_from_locations([rally_loc])
1774
1775
self.set_parameter("TKOFF_ALT", 100)
1776
self.change_mode("TAKEOFF")
1777
self.wait_ready_to_arm()
1778
self.arm_vehicle()
1779
self.set_parameter("TERRAIN_FOLLOW", 1)
1780
self.wait_altitude(90, 120, timeout=30, relative=True)
1781
self.progress("Done takeoff")
1782
1783
self.install_message_hook_context(terrain_following_above_80m)
1784
1785
self.change_mode("GUIDED")
1786
self.send_do_reposition(guided_loc, frame=mavutil.mavlink.MAV_FRAME_GLOBAL_TERRAIN_ALT)
1787
self.progress("Flying to guided location")
1788
self.wait_location(
1789
guided_loc,
1790
accuracy=200,
1791
timeout=600,
1792
height_accuracy=10,
1793
)
1794
1795
self.progress("Reached guided location")
1796
self.set_parameter("RALLY_LIMIT_KM", 50)
1797
self.change_mode("RTL")
1798
self.progress("Flying to rally point")
1799
self.wait_location(
1800
rally_loc,
1801
accuracy=200,
1802
timeout=600,
1803
height_accuracy=10,
1804
)
1805
self.progress("Reached rally point with TERRAIN_FOLLOW")
1806
1807
# Fly back to guided location
1808
self.change_mode("GUIDED")
1809
self.send_do_reposition(guided_loc, frame=mavutil.mavlink.MAV_FRAME_GLOBAL_TERRAIN_ALT)
1810
self.progress("Flying to back to guided location")
1811
1812
# Disable terrain following and re-load rally point with relative to terrain altitude
1813
self.set_parameter("TERRAIN_FOLLOW", 0)
1814
1815
rally_item = [self.create_MISSION_ITEM_INT(
1816
mavutil.mavlink.MAV_CMD_NAV_RALLY_POINT,
1817
x=int(rally_loc.lat*1e7),
1818
y=int(rally_loc.lng*1e7),
1819
z=rally_loc.alt,
1820
frame=mavutil.mavlink.MAV_FRAME_GLOBAL_TERRAIN_ALT,
1821
mission_type=mavutil.mavlink.MAV_MISSION_TYPE_RALLY
1822
)]
1823
self.correct_wp_seq_numbers(rally_item)
1824
self.check_rally_upload_download(rally_item)
1825
1826
# Once back at guided location re-trigger RTL
1827
self.wait_location(
1828
guided_loc,
1829
accuracy=200,
1830
timeout=600,
1831
height_accuracy=10,
1832
)
1833
1834
self.change_mode("RTL")
1835
self.progress("Flying to rally point")
1836
self.wait_location(
1837
rally_loc,
1838
accuracy=200,
1839
timeout=600,
1840
height_accuracy=10,
1841
)
1842
self.progress("Reached rally point with terrain alt frame")
1843
1844
self.context_pop()
1845
self.disarm_vehicle(force=True)
1846
self.reboot_sitl()
1847
1848
def Parachute(self):
1849
'''Test Parachute'''
1850
self.set_rc(9, 1000)
1851
self.set_parameters({
1852
"CHUTE_ENABLED": 1,
1853
"CHUTE_TYPE": 10,
1854
"SERVO9_FUNCTION": 27,
1855
"SIM_PARA_ENABLE": 1,
1856
"SIM_PARA_PIN": 9,
1857
})
1858
1859
self.load_mission("plane-parachute-mission.txt")
1860
self.set_current_waypoint(1)
1861
self.change_mode('AUTO')
1862
self.wait_ready_to_arm()
1863
self.arm_vehicle()
1864
self.wait_statustext("BANG", timeout=60)
1865
self.disarm_vehicle(force=True)
1866
self.reboot_sitl()
1867
1868
def ParachuteSinkRate(self):
1869
'''Test Parachute (SinkRate triggering)'''
1870
self.set_rc(9, 1000)
1871
self.set_parameters({
1872
"CHUTE_ENABLED": 1,
1873
"CHUTE_TYPE": 10,
1874
"SERVO9_FUNCTION": 27,
1875
"SIM_PARA_ENABLE": 1,
1876
"SIM_PARA_PIN": 9,
1877
"CHUTE_CRT_SINK": 9,
1878
})
1879
1880
self.progress("Takeoff")
1881
self.takeoff(alt=300)
1882
1883
self.progress("Diving")
1884
self.set_rc(2, 2000)
1885
self.wait_statustext("BANG", timeout=60)
1886
1887
self.disarm_vehicle(force=True)
1888
self.reboot_sitl()
1889
1890
def run_subtest(self, desc, func):
1891
self.start_subtest(desc)
1892
func()
1893
1894
def fly_ahrs2_test(self):
1895
'''check secondary estimator is looking OK'''
1896
1897
ahrs2 = self.assert_receive_message('AHRS2', verbose=1)
1898
gpi = self.assert_receive_message('GLOBAL_POSITION_INT', verbose=1)
1899
if self.get_distance_int(gpi, ahrs2) > 10:
1900
raise NotAchievedException("Secondary location looks bad")
1901
1902
self.check_attitudes_match()
1903
1904
def MainFlight(self):
1905
'''Lots of things in one flight'''
1906
self.change_mode('MANUAL')
1907
1908
self.progress("Asserting we do support transfer of fence via mission item protocol")
1909
self.assert_capability(mavutil.mavlink.MAV_PROTOCOL_CAPABILITY_MISSION_FENCE)
1910
1911
self.run_subtest("Takeoff", self.takeoff)
1912
1913
self.run_subtest("Set Attitude Target", self.set_attitude_target)
1914
1915
self.run_subtest("Fly left circuit", self.fly_left_circuit)
1916
1917
self.run_subtest("Left roll", lambda: self.axial_left_roll(1))
1918
1919
self.run_subtest("Inside loop", self.inside_loop)
1920
1921
self.run_subtest("Stablize test", self.test_stabilize)
1922
1923
self.run_subtest("ACRO test", self.test_acro)
1924
1925
self.run_subtest("FBWB test", self.test_FBWB)
1926
1927
self.run_subtest("CRUISE test", lambda: self.test_FBWB(mode='CRUISE'))
1928
1929
self.run_subtest("RTL test", self.fly_RTL)
1930
1931
self.run_subtest("LOITER test", self.fly_LOITER)
1932
1933
self.run_subtest("CIRCLE test", self.fly_CIRCLE)
1934
1935
self.run_subtest("AHRS2 test", self.fly_ahrs2_test)
1936
1937
self.run_subtest("Mission test",
1938
lambda: self.fly_mission("ap1.txt", strict=False))
1939
1940
def PitotBlockage(self):
1941
'''Test detection and isolation of a blocked pitot tube'''
1942
self.set_parameters({
1943
"ARSPD_OPTIONS": 15,
1944
"ARSPD_USE": 1,
1945
"SIM_WIND_SPD": 7,
1946
"SIM_WIND_DIR": 0,
1947
"ARSPD_WIND_MAX": 15,
1948
})
1949
self.takeoff(alt=50, mode='TAKEOFF')
1950
# simulate the effect of a blocked pitot tube
1951
self.set_parameter("ARSPD_RATIO", 0.1)
1952
self.delay_sim_time(10)
1953
if (self.get_parameter("ARSPD_USE") == 0):
1954
self.progress("Faulty Sensor Disabled")
1955
else:
1956
raise NotAchievedException("Airspeed Sensor Not Disabled")
1957
self.delay_sim_time(20)
1958
# simulate the effect of blockage partially clearing
1959
self.set_parameter("ARSPD_RATIO", 1.0)
1960
self.delay_sim_time(60)
1961
if (self.get_parameter("ARSPD_USE") == 0):
1962
self.progress("Faulty Sensor Remains Disabled")
1963
else:
1964
raise NotAchievedException("Fault Sensor Re-Enabled")
1965
# simulate the effect of blockage fully clearing
1966
self.set_parameter("ARSPD_RATIO", 2.0)
1967
self.delay_sim_time(60)
1968
if (self.get_parameter("ARSPD_USE") == 1):
1969
self.progress("Sensor Re-Enabled")
1970
else:
1971
raise NotAchievedException("Airspeed Sensor Not Re-Enabled")
1972
self.fly_home_land_and_disarm()
1973
1974
def AIRSPEED_AUTOCAL(self):
1975
'''Test AIRSPEED_AUTOCAL'''
1976
self.progress("Ensure no AIRSPEED_AUTOCAL on ground")
1977
self.set_parameters({
1978
"ARSPD_AUTOCAL": 1,
1979
"ARSPD_PIN": 2,
1980
"ARSPD_RATIO": 0,
1981
"ARSPD2_RATIO": 4,
1982
"ARSPD2_TYPE": 3, # MS5525
1983
"ARSPD2_BUS": 1,
1984
"ARSPD2_AUTOCAL": 1,
1985
"SIM_ARSPD2_OFS": 1900, # default is 2013
1986
1987
"RTL_AUTOLAND": 1,
1988
})
1989
self.context_collect('STATUSTEXT')
1990
self.reboot_sitl()
1991
1992
self.assert_not_receive_message('AIRSPEED_AUTOCAL', timeout=5)
1993
1994
# these are boot-time calibration messages:
1995
self.wait_statustext('Airspeed 1 calibrated', check_context=True, timeout=30)
1996
self.wait_statustext('Airspeed 2 calibrated', check_context=True)
1997
1998
mission_filepath = "flaps.txt"
1999
self.load_mission(mission_filepath)
2000
self.wait_ready_to_arm()
2001
self.arm_vehicle()
2002
self.change_mode("AUTO")
2003
self.progress("Ensure AIRSPEED_AUTOCAL in air")
2004
self.assert_receive_message('AIRSPEED_AUTOCAL')
2005
self.wait_statustext("Airspeed 0 ratio reset", check_context=True, timeout=70)
2006
self.wait_statustext("Airspeed 1 ratio reset", check_context=True, timeout=70)
2007
self.fly_home_land_and_disarm()
2008
2009
def deadreckoning_main(self, disable_airspeed_sensor=False):
2010
self.context_push()
2011
self.set_parameter("EK3_OPTIONS", 1)
2012
self.set_parameter("AHRS_OPTIONS", 3)
2013
self.set_parameter("LOG_REPLAY", 1)
2014
self.reboot_sitl()
2015
self.wait_ready_to_arm()
2016
2017
if disable_airspeed_sensor:
2018
max_allowed_divergence = 300
2019
else:
2020
max_allowed_divergence = 150
2021
self.install_message_hook_context(vehicle_test_suite.TestSuite.ValidateGlobalPositionIntAgainstSimState(self, max_allowed_divergence=max_allowed_divergence)) # noqa
2022
2023
try:
2024
# wind is from the West:
2025
self.set_parameter("SIM_WIND_DIR", 270)
2026
# light winds:
2027
self.set_parameter("SIM_WIND_SPD", 10)
2028
if disable_airspeed_sensor:
2029
self.set_parameter("ARSPD_USE", 0)
2030
2031
self.takeoff(50)
2032
loc = self.mav.location()
2033
self.location_offset_ne(loc, 500, 500)
2034
self.run_cmd_int(
2035
mavutil.mavlink.MAV_CMD_DO_REPOSITION,
2036
p1=0,
2037
p2=mavutil.mavlink.MAV_DO_REPOSITION_FLAGS_CHANGE_MODE,
2038
p5=int(loc.lat * 1e7),
2039
p6=int(loc.lng * 1e7),
2040
p7=100, # alt
2041
frame=mavutil.mavlink.MAV_FRAME_GLOBAL_RELATIVE_ALT_INT,
2042
)
2043
self.wait_location(loc, accuracy=100)
2044
self.progress("Orbit with GPS and learn wind")
2045
# allow longer to learn wind if there is no airspeed sensor
2046
if disable_airspeed_sensor:
2047
self.delay_sim_time(60)
2048
else:
2049
self.delay_sim_time(20)
2050
self.set_parameter("SIM_GPS1_ENABLE", 0)
2051
self.progress("Continue orbit without GPS")
2052
self.delay_sim_time(20)
2053
self.change_mode("RTL")
2054
self.wait_distance_to_home(100, 200, timeout=200)
2055
# go into LOITER to create additonal time for a GPS re-enable test
2056
self.change_mode("LOITER")
2057
self.set_parameter("SIM_GPS1_ENABLE", 1)
2058
t_enabled = self.get_sim_time()
2059
# The EKF should wait for GPS checks to pass when we are still able to navigate using dead reckoning
2060
# to prevent bad GPS being used when coming back after loss of lock due to interence.
2061
self.wait_ekf_flags(mavutil.mavlink.ESTIMATOR_POS_HORIZ_ABS, 0, timeout=15)
2062
if self.get_sim_time() < (t_enabled+9):
2063
raise NotAchievedException("GPS use re-started too quickly")
2064
# wait for EKF and vehicle position to stabilise, then test response to jamming
2065
self.delay_sim_time(20)
2066
2067
self.set_parameter("AHRS_OPTIONS", 1)
2068
self.set_parameter("SIM_GPS1_JAM", 1)
2069
self.delay_sim_time(10)
2070
self.set_parameter("SIM_GPS1_JAM", 0)
2071
t_enabled = self.get_sim_time()
2072
# The EKF should wait for GPS checks to pass when we are still able to navigate using dead reckoning
2073
# to prevent bad GPS being used when coming back after loss of lock due to interence.
2074
# The EKF_STATUS_REPORT does not tell us when the good to align check passes, so the minimum time
2075
# value of 3.0 seconds is an arbitrary value set on inspection of dataflash logs from this test
2076
self.wait_ekf_flags(mavutil.mavlink.ESTIMATOR_POS_HORIZ_ABS, 0, timeout=15)
2077
time_since_jamming_stopped = self.get_sim_time() - t_enabled
2078
if time_since_jamming_stopped < 3:
2079
raise NotAchievedException("GPS use re-started %f sec after jamming stopped" % time_since_jamming_stopped)
2080
self.set_rc(3, 1000)
2081
self.fly_home_land_and_disarm()
2082
finally:
2083
pass
2084
2085
self.context_pop()
2086
self.reboot_sitl()
2087
2088
def Deadreckoning(self):
2089
'''Test deadreckoning support'''
2090
self.deadreckoning_main()
2091
2092
def DeadreckoningNoAirSpeed(self):
2093
'''Test deadreckoning support with no airspeed sensor'''
2094
self.deadreckoning_main(disable_airspeed_sensor=True)
2095
2096
def ClimbBeforeTurn(self):
2097
'''Test climb-before-turn'''
2098
self.wait_ready_to_arm()
2099
self.set_parameters({
2100
"FLIGHT_OPTIONS": 0,
2101
"RTL_ALTITUDE": 80,
2102
"RTL_AUTOLAND": 1,
2103
})
2104
takeoff_alt = 10
2105
self.takeoff(alt=takeoff_alt)
2106
self.change_mode("CRUISE")
2107
self.wait_distance_to_home(500, 1000, timeout=60)
2108
self.change_mode("RTL")
2109
expected_alt = self.get_parameter("RTL_ALTITUDE")
2110
2111
home = self.home_position_as_mav_location()
2112
distance = self.get_distance(home, self.mav.location())
2113
2114
self.wait_altitude(expected_alt - 10, expected_alt + 10, relative=True, timeout=80)
2115
2116
new_distance = self.get_distance(home, self.mav.location())
2117
# We should be closer to home.
2118
if new_distance > distance:
2119
raise NotAchievedException(
2120
"Expected to be closer to home (was %fm, now %fm)."
2121
% (distance, new_distance)
2122
)
2123
2124
self.fly_home_land_and_disarm()
2125
self.set_current_waypoint(0, check_afterwards=False)
2126
2127
self.change_mode("MANUAL")
2128
self.set_rc(3, 1000)
2129
2130
self.wait_ready_to_arm()
2131
self.set_parameters({
2132
"FLIGHT_OPTIONS": 16,
2133
"RTL_ALTITUDE": 100,
2134
})
2135
self.takeoff(alt=takeoff_alt)
2136
self.change_mode("CRUISE")
2137
self.wait_distance_to_home(500, 1000, timeout=60)
2138
self.change_mode("RTL")
2139
2140
home = self.home_position_as_mav_location()
2141
distance = self.get_distance(home, self.mav.location())
2142
2143
self.wait_altitude(expected_alt - 10, expected_alt + 10, relative=True, timeout=80)
2144
2145
new_distance = self.get_distance(home, self.mav.location())
2146
# We should be farther from to home.
2147
if new_distance < distance:
2148
raise NotAchievedException(
2149
"Expected to be farther from home (was %fm, now %fm)."
2150
% (distance, new_distance)
2151
)
2152
2153
self.fly_home_land_and_disarm(timeout=240)
2154
2155
def RTL_CLIMB_MIN(self):
2156
'''Test RTL_CLIMB_MIN'''
2157
self.wait_ready_to_arm()
2158
rtl_climb_min = 100
2159
self.set_parameter("RTL_CLIMB_MIN", rtl_climb_min)
2160
takeoff_alt = 50
2161
self.takeoff(alt=takeoff_alt)
2162
self.change_mode('CRUISE')
2163
self.wait_distance_to_home(1000, 1500, timeout=60)
2164
post_cruise_alt = self.get_altitude(relative=True)
2165
self.change_mode('RTL')
2166
expected_alt = self.get_parameter("RTL_ALTITUDE")
2167
if expected_alt == -1:
2168
expected_alt = self.get_altitude(relative=True)
2169
2170
# ensure we're about half-way-down at the half-way-home stage:
2171
self.wait_distance_to_nav_target(
2172
0,
2173
500,
2174
timeout=240,
2175
)
2176
alt = self.get_altitude(relative=True)
2177
expected_halfway_alt = expected_alt + (post_cruise_alt + rtl_climb_min - expected_alt)/2.0
2178
if abs(alt - expected_halfway_alt) > 30:
2179
raise NotAchievedException("Not half-way-down and half-way-home (want=%f got=%f" %
2180
(expected_halfway_alt, alt))
2181
self.progress("Half-way-down at half-way-home (want=%f vs got=%f)" %
2182
(expected_halfway_alt, alt))
2183
2184
rtl_radius = self.get_parameter("RTL_RADIUS")
2185
if rtl_radius == 0:
2186
rtl_radius = self.get_parameter("WP_LOITER_RAD")
2187
self.wait_distance_to_nav_target(
2188
0,
2189
rtl_radius,
2190
timeout=120,
2191
)
2192
alt = self.get_altitude(relative=True)
2193
if abs(alt - expected_alt) > 10:
2194
raise NotAchievedException(
2195
"Expected to have %fm altitude at end of RTL (got %f)" %
2196
(expected_alt, alt))
2197
self.fly_home_land_and_disarm()
2198
2199
def sample_enable_parameter(self):
2200
return "Q_ENABLE"
2201
2202
def RangeFinder(self):
2203
'''Test RangeFinder Basic Functionality'''
2204
self.progress("Making sure we don't ordinarily get RANGEFINDER")
2205
self.assert_not_receive_message('RANGEFINDER')
2206
2207
self.set_analog_rangefinder_parameters()
2208
2209
self.reboot_sitl()
2210
2211
'''ensure rangefinder gives height-above-ground'''
2212
self.load_mission("plane-gripper-mission.txt") # borrow this
2213
self.set_parameter("RTL_AUTOLAND", 1)
2214
self.set_current_waypoint(1)
2215
self.change_mode('AUTO')
2216
self.wait_ready_to_arm()
2217
self.arm_vehicle()
2218
self.wait_waypoint(5, 5, max_dist=100)
2219
rf = self.assert_receive_message('RANGEFINDER')
2220
gpi = self.assert_receive_message('GLOBAL_POSITION_INT')
2221
if abs(rf.distance - gpi.relative_alt/1000.0) > 3:
2222
raise NotAchievedException(
2223
"rangefinder alt (%s) disagrees with global-position-int.relative_alt (%s)" %
2224
(rf.distance, gpi.relative_alt/1000.0))
2225
self.wait_statustext("Auto disarmed", timeout=60)
2226
2227
self.progress("Ensure RFND messages in log")
2228
if not self.current_onboard_log_contains_message("RFND"):
2229
raise NotAchievedException("No RFND messages in log")
2230
2231
def rc_defaults(self):
2232
ret = super(AutoTestPlane, self).rc_defaults()
2233
ret[3] = 1000
2234
ret[8] = 1800
2235
return ret
2236
2237
def initial_mode_switch_mode(self):
2238
return "MANUAL"
2239
2240
def default_mode(self):
2241
return "MANUAL"
2242
2243
def PIDTuning(self):
2244
'''Test PID Tuning'''
2245
self.change_mode("FBWA") # we don't update PIDs in MANUAL
2246
super(AutoTestPlane, self).PIDTuning()
2247
2248
def AuxModeSwitch(self):
2249
'''Set modes via auxswitches'''
2250
self.set_parameter("FLTMODE1", 1) # circle
2251
self.set_rc(8, 950)
2252
self.wait_mode("CIRCLE")
2253
self.set_rc(9, 1000)
2254
self.set_rc(10, 1000)
2255
self.set_parameters({
2256
"RC9_OPTION": 4, # RTL
2257
"RC10_OPTION": 55, # guided
2258
})
2259
self.set_rc(9, 1900)
2260
self.wait_mode("RTL")
2261
self.set_rc(10, 1900)
2262
self.wait_mode("GUIDED")
2263
2264
self.progress("resetting both switches - should go back to CIRCLE")
2265
self.set_rc(9, 1000)
2266
self.set_rc(10, 1000)
2267
self.wait_mode("CIRCLE")
2268
2269
self.set_rc(9, 1900)
2270
self.wait_mode("RTL")
2271
self.set_rc(10, 1900)
2272
self.wait_mode("GUIDED")
2273
2274
self.progress("Resetting switch should repoll mode switch")
2275
self.set_rc(10, 1000) # this re-polls the mode switch
2276
self.wait_mode("CIRCLE")
2277
self.set_rc(9, 1000)
2278
2279
def wait_for_collision_threat_to_clear(self):
2280
'''wait to get a "clear" collision message", then slurp remaining
2281
messages'''
2282
last_collision = self.get_sim_time()
2283
while True:
2284
now = self.get_sim_time()
2285
if now - last_collision > 5:
2286
return
2287
self.progress("Waiting for collision message")
2288
m = self.mav.recv_match(type='COLLISION', blocking=True, timeout=1)
2289
self.progress("Got (%s)" % str(m))
2290
if m is None:
2291
continue
2292
last_collision = now
2293
2294
def SimADSB(self):
2295
'''Tests to ensure simulated ADSB sensor continues to function'''
2296
self.set_parameters({
2297
"SIM_ADSB_COUNT": 1,
2298
"ADSB_TYPE": 1,
2299
})
2300
self.reboot_sitl()
2301
self.assert_receive_message('ADSB_VEHICLE', timeout=30)
2302
2303
def ADSBResumeActionResumeLoiter(self):
2304
'''ensure we resume auto mission or enter loiter'''
2305
self.set_parameters({
2306
"ADSB_TYPE": 1,
2307
"AVD_ENABLE": 1,
2308
"AVD_F_ACTION": mavutil.mavlink.MAV_COLLISION_ACTION_MOVE_HORIZONTALLY,
2309
"AVD_F_RCVRY": 3, # resume auto or loiter
2310
})
2311
self.reboot_sitl()
2312
self.takeoff(50)
2313
# fly North, create thread to east, wait for flying east
2314
self.start_subtest("Testing loiter resume")
2315
self.reach_heading_manual(0)
2316
here = self.mav.location()
2317
self.test_adsb_send_threatening_adsb_message(here, offset_ne=(0, 30))
2318
self.wait_mode('AVOID_ADSB')
2319
# recovery has the vehicle circling a point... but we don't
2320
# know which point. So wait 'til it looks like it is
2321
# circling, then grab the point, then check we're circling
2322
# it...
2323
self.wait_heading(290)
2324
self.wait_heading(300)
2325
dest = self.position_target_loc()
2326
REALLY_BAD_FUDGE_FACTOR = 1.25 # FIXME
2327
expected_radius = REALLY_BAD_FUDGE_FACTOR * self.get_parameter('WP_LOITER_RAD')
2328
self.wait_circling_point_with_radius(dest, expected_radius)
2329
2330
self.start_subtest("Testing mission resume")
2331
self.reach_heading_manual(270)
2332
self.load_generic_mission("CMAC-circuit.txt", strict=False)
2333
self.change_mode('AUTO')
2334
self.wait_current_waypoint(2)
2335
self.test_adsb_send_threatening_adsb_message(here, offset_ne=(0, 30))
2336
self.wait_mode('AVOID_ADSB')
2337
self.wait_mode('AUTO')
2338
2339
self.fly_home_land_and_disarm()
2340
2341
def ADSBFailActionRTL(self):
2342
'''test ADSB avoidance action of RTL'''
2343
# message ADSB_VEHICLE 37 -353632614 1491652305 0 584070 0 0 0 "bob" 3 1 255 17
2344
self.set_parameter("RC12_OPTION", 38) # avoid-adsb
2345
self.set_rc(12, 2000)
2346
self.set_parameters({
2347
"ADSB_TYPE": 1,
2348
"AVD_ENABLE": 1,
2349
"AVD_F_ACTION": mavutil.mavlink.MAV_COLLISION_ACTION_RTL,
2350
})
2351
self.reboot_sitl()
2352
self.wait_ready_to_arm()
2353
here = self.mav.location()
2354
self.change_mode("FBWA")
2355
self.delay_sim_time(2) # TODO: work out why this is required...
2356
self.test_adsb_send_threatening_adsb_message(here)
2357
self.progress("Waiting for collision message")
2358
m = self.assert_receive_message('COLLISION', timeout=4)
2359
if m.threat_level != 2:
2360
raise NotAchievedException("Expected some threat at least")
2361
if m.action != mavutil.mavlink.MAV_COLLISION_ACTION_RTL:
2362
raise NotAchievedException("Incorrect action; want=%u got=%u" %
2363
(mavutil.mavlink.MAV_COLLISION_ACTION_RTL, m.action))
2364
self.wait_mode("RTL")
2365
2366
self.progress("Sending far-away ABSD_VEHICLE message")
2367
self.mav.mav.adsb_vehicle_send(
2368
37, # ICAO address
2369
int(here.lat+1 * 1e7),
2370
int(here.lng * 1e7),
2371
mavutil.mavlink.ADSB_ALTITUDE_TYPE_PRESSURE_QNH,
2372
int(here.alt*1000 + 10000), # 10m up
2373
0, # heading in cdeg
2374
0, # horizontal velocity cm/s
2375
0, # vertical velocity cm/s
2376
"bob".encode("ascii"), # callsign
2377
mavutil.mavlink.ADSB_EMITTER_TYPE_LIGHT,
2378
1, # time since last communication
2379
65535, # flags
2380
17 # squawk
2381
)
2382
self.wait_for_collision_threat_to_clear()
2383
self.change_mode("FBWA")
2384
2385
self.progress("Disabling ADSB-avoidance with RC channel")
2386
self.set_rc(12, 1000)
2387
self.delay_sim_time(1) # let the switch get polled
2388
self.test_adsb_send_threatening_adsb_message(here)
2389
m = self.mav.recv_match(type='COLLISION', blocking=True, timeout=4)
2390
self.progress("Got (%s)" % str(m))
2391
if m is not None:
2392
raise NotAchievedException("Got collision message when I shouldn't have")
2393
2394
def GuidedRequest(self, target_system=1, target_component=1):
2395
'''Test handling of MISSION_ITEM in guided mode'''
2396
self.progress("Takeoff")
2397
self.takeoff(alt=50)
2398
self.set_rc(3, 1500)
2399
self.start_subtest("Ensure command bounced outside guided mode")
2400
desired_relative_alt = 33
2401
loc = self.mav.location()
2402
self.location_offset_ne(loc, 300, 300)
2403
loc.alt += desired_relative_alt
2404
self.mav.mav.mission_item_int_send(
2405
target_system,
2406
target_component,
2407
0, # seq
2408
mavutil.mavlink.MAV_FRAME_GLOBAL,
2409
mavutil.mavlink.MAV_CMD_NAV_WAYPOINT,
2410
2, # current - guided-mode request
2411
0, # autocontinue
2412
0, # p1
2413
0, # p2
2414
0, # p3
2415
0, # p4
2416
int(loc.lat * 1e7), # latitude
2417
int(loc.lng * 1e7), # longitude
2418
loc.alt, # altitude
2419
mavutil.mavlink.MAV_MISSION_TYPE_MISSION)
2420
m = self.assert_receive_message('MISSION_ACK', timeout=5)
2421
if m.type != mavutil.mavlink.MAV_MISSION_ERROR:
2422
raise NotAchievedException("Did not get appropriate error")
2423
2424
self.start_subtest("Enter guided and flying somewhere constant")
2425
self.change_mode("GUIDED")
2426
self.mav.mav.mission_item_int_send(
2427
target_system,
2428
target_component,
2429
0, # seq
2430
mavutil.mavlink.MAV_FRAME_GLOBAL_RELATIVE_ALT,
2431
mavutil.mavlink.MAV_CMD_NAV_WAYPOINT,
2432
2, # current - guided-mode request
2433
0, # autocontinue
2434
0, # p1
2435
0, # p2
2436
0, # p3
2437
0, # p4
2438
int(loc.lat * 1e7), # latitude
2439
int(loc.lng * 1e7), # longitude
2440
desired_relative_alt, # altitude
2441
mavutil.mavlink.MAV_MISSION_TYPE_MISSION)
2442
m = self.assert_receive_message('MISSION_ACK', timeout=5)
2443
if m.type != mavutil.mavlink.MAV_MISSION_ACCEPTED:
2444
raise NotAchievedException("Did not get accepted response")
2445
self.wait_location(loc, accuracy=100) # based on loiter radius
2446
self.wait_altitude(altitude_min=desired_relative_alt-3,
2447
altitude_max=desired_relative_alt+3,
2448
relative=True,
2449
timeout=30)
2450
2451
self.start_subtest("changing alt with mission item in guided mode")
2452
2453
# test changing alt only - NOTE - this is still a
2454
# NAV_WAYPOINT, not a changel-alt request!
2455
desired_relative_alt = desired_relative_alt + 50
2456
self.mav.mav.mission_item_int_send(
2457
target_system,
2458
target_component,
2459
0, # seq
2460
mavutil.mavlink.MAV_FRAME_GLOBAL_RELATIVE_ALT,
2461
mavutil.mavlink.MAV_CMD_NAV_WAYPOINT,
2462
3, # current - change-alt request
2463
0, # autocontinue
2464
0, # p1
2465
0, # p2
2466
0, # p3
2467
0, # p4
2468
0, # latitude
2469
0,
2470
desired_relative_alt, # altitude
2471
mavutil.mavlink.MAV_MISSION_TYPE_MISSION)
2472
2473
self.wait_altitude(altitude_min=desired_relative_alt-3,
2474
altitude_max=desired_relative_alt+3,
2475
relative=True,
2476
timeout=30)
2477
2478
self.fly_home_land_and_disarm()
2479
2480
def LOITER(self):
2481
'''Test Loiter mode'''
2482
# first test old loiter behavour
2483
self.set_parameter("FLIGHT_OPTIONS", 0)
2484
self.takeoff(alt=200)
2485
self.set_rc(3, 1500)
2486
self.change_mode("LOITER")
2487
self.progress("Doing a bit of loitering to start with")
2488
tstart = self.get_sim_time()
2489
while True:
2490
now = self.get_sim_time_cached()
2491
if now - tstart > 60:
2492
break
2493
m = self.assert_receive_message('VFR_HUD')
2494
new_throttle = m.throttle
2495
alt = m.alt
2496
m = self.assert_receive_message('ATTITUDE', timeout=5)
2497
pitch = math.degrees(m.pitch)
2498
self.progress("Pitch:%f throttle:%u alt:%f" % (pitch, new_throttle, alt))
2499
m = self.assert_receive_message('VFR_HUD', timeout=5)
2500
initial_throttle = m.throttle
2501
initial_alt = m.alt
2502
self.progress("Initial throttle: %u" % initial_throttle)
2503
# pitch down, ensure throttle decreases:
2504
rc2_max = self.get_parameter("RC2_MAX")
2505
self.set_rc(2, int(rc2_max))
2506
tstart = self.get_sim_time()
2507
while True:
2508
now = self.get_sim_time_cached()
2509
'''stick-mixing is pushing the aircraft down. It doesn't want to go
2510
down (the target loiter altitude hasn't changed), so it
2511
tries to add energy by increasing the throttle.
2512
'''
2513
if now - tstart > 60:
2514
raise NotAchievedException("Did not see increase in throttle")
2515
m = self.assert_receive_message('VFR_HUD', timeout=5)
2516
new_throttle = m.throttle
2517
alt = m.alt
2518
m = self.assert_receive_message('ATTITUDE', timeout=5)
2519
pitch = math.degrees(m.pitch)
2520
self.progress("Pitch:%f throttle:%u alt:%f" % (pitch, new_throttle, alt))
2521
if new_throttle - initial_throttle > 20:
2522
self.progress("Throttle delta achieved")
2523
break
2524
self.progress("Centering elevator and ensuring we get back to loiter altitude")
2525
self.set_rc(2, 1500)
2526
self.wait_altitude(initial_alt-1, initial_alt+1)
2527
# Test new loiter behavour
2528
self.set_parameter("FLIGHT_OPTIONS", 1 << 12)
2529
# should decend at max stick
2530
self.set_rc(2, int(rc2_max))
2531
self.wait_altitude(initial_alt - 110, initial_alt - 90, timeout=90)
2532
# should not climb back at mid stick
2533
self.set_rc(2, 1500)
2534
self.delay_sim_time(60)
2535
self.wait_altitude(initial_alt - 110, initial_alt - 90)
2536
# should climb at min stick
2537
self.set_rc(2, 1100)
2538
self.wait_altitude(initial_alt - 10, initial_alt + 10, timeout=90)
2539
# return stick to center and fly home
2540
self.set_rc(2, 1500)
2541
self.fly_home_land_and_disarm()
2542
2543
def CPUFailsafe(self):
2544
'''In lockup Plane should copy RC inputs to RC outputs'''
2545
self.plane_CPUFailsafe()
2546
2547
def LargeMissions(self):
2548
'''Test Manipulation of Large missions'''
2549
self.load_mission("Kingaroy-vlarge.txt", strict=False)
2550
self.load_mission("Kingaroy-vlarge2.txt", strict=False)
2551
2552
def Soaring(self):
2553
'''Test Soaring feature'''
2554
2555
model = "plane-soaring"
2556
2557
self.customise_SITL_commandline(
2558
[],
2559
model=model,
2560
defaults_filepath=self.model_defaults_filepath(model),
2561
wipe=True)
2562
2563
self.load_mission('CMAC-soar.txt', strict=False)
2564
2565
# Enable thermalling RC
2566
rc_chan = 0
2567
for i in range(8):
2568
rcx_option = self.get_parameter('RC{0}_OPTION'.format(i+1))
2569
if rcx_option == 88:
2570
rc_chan = i+1
2571
break
2572
2573
if rc_chan == 0:
2574
raise NotAchievedException("Did not find soaring enable channel option.")
2575
2576
self.set_rc_from_map({
2577
rc_chan: 1900,
2578
})
2579
2580
self.set_parameters({
2581
"SOAR_VSPEED": 0.55,
2582
"SOAR_MIN_THML_S": 25,
2583
})
2584
2585
self.set_current_waypoint(1)
2586
self.change_mode('AUTO')
2587
self.wait_ready_to_arm()
2588
self.arm_vehicle()
2589
2590
# Wait to detect thermal
2591
self.progress("Waiting for thermal")
2592
self.wait_mode('THERMAL', timeout=600)
2593
2594
# Wait to climb to SOAR_ALT_MAX
2595
self.progress("Waiting for climb to max altitude")
2596
alt_max = self.get_parameter('SOAR_ALT_MAX')
2597
self.wait_altitude(alt_max-10, alt_max, timeout=600, relative=True)
2598
2599
# Wait for AUTO
2600
self.progress("Waiting for AUTO mode")
2601
self.wait_mode('AUTO')
2602
2603
# Disable thermals
2604
self.set_parameter("SIM_THML_SCENARI", 0)
2605
2606
# Wait to descend to SOAR_ALT_MIN
2607
self.progress("Waiting for glide to min altitude")
2608
alt_min = self.get_parameter('SOAR_ALT_MIN')
2609
self.wait_altitude(alt_min-10, alt_min, timeout=600, relative=True)
2610
2611
self.progress("Waiting for throttle up")
2612
self.wait_servo_channel_value(3, 1200, timeout=5, comparator=operator.gt)
2613
2614
self.progress("Waiting for climb to cutoff altitude")
2615
alt_ctf = self.get_parameter('SOAR_ALT_CUTOFF')
2616
self.wait_altitude(alt_ctf-10, alt_ctf, timeout=600, relative=True)
2617
2618
# Allow time to suppress throttle and start descent.
2619
self.delay_sim_time(20)
2620
2621
# Now set FBWB mode
2622
self.change_mode('FBWB')
2623
self.delay_sim_time(5)
2624
2625
# Now disable soaring (should hold altitude)
2626
self.set_parameter("SOAR_ENABLE", 0)
2627
self.delay_sim_time(10)
2628
2629
# And reenable. This should force throttle-down
2630
self.set_parameter("SOAR_ENABLE", 1)
2631
self.delay_sim_time(10)
2632
2633
# Now wait for descent and check throttle up
2634
self.wait_altitude(alt_min-10, alt_min, timeout=600, relative=True)
2635
2636
self.progress("Waiting for climb")
2637
self.wait_altitude(alt_ctf-10, alt_ctf, timeout=600, relative=True)
2638
2639
# Back to auto
2640
self.change_mode('AUTO')
2641
2642
# Reenable thermals
2643
self.set_parameter("SIM_THML_SCENARI", 1)
2644
2645
# Disable soaring using RC channel.
2646
self.set_rc(rc_chan, 1100)
2647
2648
# Wait to get back to waypoint before thermal.
2649
self.progress("Waiting to get back to position")
2650
self.wait_current_waypoint(3, timeout=1200)
2651
2652
# Enable soaring with mode changes suppressed)
2653
self.set_rc(rc_chan, 1500)
2654
2655
# Make sure this causes throttle down.
2656
self.wait_servo_channel_value(3, 1200, timeout=3, comparator=operator.lt)
2657
2658
self.progress("Waiting for next WP with no thermalling")
2659
self.wait_waypoint(4, 4, timeout=1200, max_dist=120)
2660
2661
# Disarm
2662
self.disarm_vehicle_expect_fail()
2663
2664
self.progress("Mission OK")
2665
2666
def SpeedToFly(self):
2667
'''Test soaring speed-to-fly'''
2668
2669
model = "plane-soaring"
2670
2671
self.customise_SITL_commandline(
2672
[],
2673
model=model,
2674
defaults_filepath=self.model_defaults_filepath(model),
2675
wipe=True)
2676
2677
self.load_mission('CMAC-soar.txt', strict=False)
2678
2679
self.set_parameters({
2680
"SIM_THML_SCENARI": 0, # Turn off environmental thermals.
2681
"SOAR_ALT_MAX": 1000, # remove source of random failure
2682
})
2683
2684
# Get thermalling RC channel
2685
rc_chan = 0
2686
for i in range(8):
2687
rcx_option = self.get_parameter('RC{0}_OPTION'.format(i+1))
2688
if rcx_option == 88:
2689
rc_chan = i+1
2690
break
2691
2692
if rc_chan == 0:
2693
raise NotAchievedException("Did not find soaring enable channel option.")
2694
2695
# Disable soaring
2696
self.set_rc(rc_chan, 1100)
2697
2698
self.set_current_waypoint(1)
2699
self.change_mode('AUTO')
2700
self.wait_ready_to_arm()
2701
self.arm_vehicle()
2702
2703
# Wait for to 400m before starting.
2704
self.wait_altitude(390, 400, timeout=600, relative=True)
2705
2706
# Wait 10s to stabilize.
2707
self.delay_sim_time(30)
2708
2709
# Enable soaring (no automatic thermalling)
2710
self.set_rc(rc_chan, 1500)
2711
2712
self.set_parameters({
2713
"SOAR_CRSE_ARSPD": -1, # Enable speed to fly.
2714
"SOAR_VSPEED": 1, # Set appropriate McCready.
2715
"SIM_WIND_SPD": 0,
2716
})
2717
2718
self.progress('Waiting a few seconds before determining the "trim" airspeed.')
2719
self.delay_sim_time(20)
2720
m = self.assert_receive_message('VFR_HUD')
2721
trim_airspeed = m.airspeed
2722
self.progress("Using trim_airspeed=%f" % (trim_airspeed,))
2723
2724
min_airspeed = self.get_parameter("AIRSPEED_MIN")
2725
max_airspeed = self.get_parameter("AIRSPEED_MAX")
2726
2727
if trim_airspeed > max_airspeed:
2728
raise NotAchievedException("trim airspeed > max_airspeed (%f>%f)" %
2729
(trim_airspeed, max_airspeed))
2730
if trim_airspeed < min_airspeed:
2731
raise NotAchievedException("trim airspeed < min_airspeed (%f<%f)" %
2732
(trim_airspeed, min_airspeed))
2733
2734
self.progress("Adding updraft")
2735
self.set_parameters({
2736
"SIM_WIND_SPD": 1,
2737
'SIM_WIND_DIR_Z': 90,
2738
})
2739
self.progress("Waiting for vehicle to move slower in updraft")
2740
self.wait_airspeed(0, trim_airspeed-0.5, minimum_duration=10, timeout=120)
2741
2742
self.progress("Adding downdraft")
2743
self.set_parameter('SIM_WIND_DIR_Z', -90)
2744
self.progress("Waiting for vehicle to move faster in downdraft")
2745
self.wait_airspeed(trim_airspeed+0.5, trim_airspeed+100, minimum_duration=10, timeout=120)
2746
2747
self.progress("Zeroing wind and increasing McCready")
2748
self.set_parameters({
2749
"SIM_WIND_SPD": 0,
2750
"SOAR_VSPEED": 2,
2751
})
2752
self.progress("Waiting for airspeed to increase with higher VSPEED")
2753
self.wait_airspeed(trim_airspeed+0.5, trim_airspeed+100, minimum_duration=10, timeout=120)
2754
2755
# mcReady tests don't work ATM, so just return early:
2756
# takes too long to land, so just make it all go away:
2757
self.disarm_vehicle(force=True)
2758
self.reboot_sitl()
2759
return
2760
2761
self.start_subtest('Test McReady values')
2762
# Disable soaring
2763
self.set_rc(rc_chan, 1100)
2764
2765
# Wait for to 400m before starting.
2766
self.wait_altitude(390, 400, timeout=600, relative=True)
2767
2768
# Enable soaring
2769
self.set_rc(rc_chan, 2000)
2770
2771
self.progress("Find airspeed with 1m/s updraft and mcready=1")
2772
self.set_parameters({
2773
"SOAR_VSPEED": 1,
2774
"SIM_WIND_SPD": 1,
2775
})
2776
self.delay_sim_time(20)
2777
m = self.assert_receive_message('VFR_HUD')
2778
mcready1_speed = m.airspeed
2779
self.progress("airspeed is %f" % mcready1_speed)
2780
2781
self.progress("Reducing McCready")
2782
self.set_parameters({
2783
"SOAR_VSPEED": 0.5,
2784
})
2785
self.progress("Waiting for airspeed to decrease with lower McReady")
2786
self.wait_airspeed(0, mcready1_speed-0.5, minimum_duration=10, timeout=120)
2787
2788
self.progress("Increasing McCready")
2789
self.set_parameters({
2790
"SOAR_VSPEED": 1.5,
2791
})
2792
self.progress("Waiting for airspeed to decrease with lower McReady")
2793
self.wait_airspeed(mcready1_speed+0.5, mcready1_speed+100, minimum_duration=10, timeout=120)
2794
2795
# takes too long to land, so just make it all go away:
2796
self.disarm_vehicle(force=True)
2797
self.reboot_sitl()
2798
2799
def AirspeedDrivers(self):
2800
'''Test AirSpeed drivers'''
2801
airspeed_sensors = [
2802
("MS5525", 3, 1),
2803
("DLVR", 7, 2),
2804
("SITL", 100, 0),
2805
]
2806
for (name, t, bus) in airspeed_sensors:
2807
self.context_push()
2808
if bus is not None:
2809
self.set_parameter("ARSPD2_BUS", bus)
2810
self.set_parameter("ARSPD2_TYPE", t)
2811
self.reboot_sitl()
2812
self.wait_ready_to_arm()
2813
self.arm_vehicle()
2814
2815
# insert listener to compare airspeeds:
2816
airspeed = [None, None]
2817
# don't start testing until we've seen real speed from
2818
# both sensors. This gets us out of the noise area.
2819
global initial_airspeed_threshold_reached
2820
initial_airspeed_threshold_reached = False
2821
2822
def check_airspeeds(mav, m):
2823
global initial_airspeed_threshold_reached
2824
m_type = m.get_type()
2825
if (m_type == 'NAMED_VALUE_FLOAT' and
2826
m.name == 'AS2'):
2827
airspeed[1] = m.value
2828
elif m_type == 'VFR_HUD':
2829
airspeed[0] = m.airspeed
2830
else:
2831
return
2832
if airspeed[0] is None or airspeed[1] is None:
2833
return
2834
if airspeed[0] < 2 or airspeed[1] < 2:
2835
# this mismatch can occur on takeoff, or when we
2836
# smack into the ground at the end of the mission
2837
return
2838
if not initial_airspeed_threshold_reached:
2839
if not (airspeed[0] > 10 or airspeed[1] > 10):
2840
return
2841
initial_airspeed_threshold_reached = True
2842
delta = abs(airspeed[0] - airspeed[1])
2843
if delta > 2:
2844
raise NotAchievedException("Airspeed mismatch (as1=%f as2=%f)" % (airspeed[0], airspeed[1]))
2845
self.install_message_hook_context(check_airspeeds)
2846
self.fly_mission("ap1.txt", strict=False)
2847
if airspeed[0] is None:
2848
raise NotAchievedException("Never saw an airspeed1")
2849
if airspeed[1] is None:
2850
raise NotAchievedException("Never saw an airspeed2")
2851
self.context_pop()
2852
self.reboot_sitl()
2853
2854
def TerrainMission(self):
2855
'''Test terrain following in mission'''
2856
self.install_terrain_handlers_context()
2857
2858
num_wp = self.load_mission("ap-terrain.txt")
2859
2860
self.wait_ready_to_arm()
2861
self.arm_vehicle()
2862
2863
global max_alt
2864
max_alt = 0
2865
2866
def record_maxalt(mav, m):
2867
global max_alt
2868
if m.get_type() != 'GLOBAL_POSITION_INT':
2869
return
2870
if m.relative_alt/1000.0 > max_alt:
2871
max_alt = m.relative_alt/1000.0
2872
2873
self.install_message_hook_context(record_maxalt)
2874
2875
self.fly_mission_waypoints(num_wp-1, mission_timeout=600)
2876
2877
if max_alt < 200:
2878
raise NotAchievedException("Did not follow terrain")
2879
2880
def Terrain(self):
2881
'''test AP_Terrain'''
2882
self.reboot_sitl() # we know the terrain height at CMAC
2883
2884
self.install_terrain_handlers_context()
2885
2886
self.wait_ready_to_arm()
2887
loc = self.mav.location()
2888
2889
lng_int = int(loc.lng * 1e7)
2890
lat_int = int(loc.lat * 1e7)
2891
2892
# FIXME: once we have a pre-populated terrain cache this
2893
# should require an instantly correct report to pass
2894
tstart = self.get_sim_time_cached()
2895
last_terrain_report_pending = -1
2896
while True:
2897
now = self.get_sim_time_cached()
2898
if now - tstart > 60:
2899
raise NotAchievedException("Did not get correct terrain report")
2900
2901
self.mav.mav.terrain_check_send(lat_int, lng_int)
2902
2903
report = self.mav.recv_match(type='TERRAIN_REPORT', blocking=True, timeout=60)
2904
self.progress(self.dump_message_verbose(report))
2905
if report.spacing != 0:
2906
break
2907
2908
# we will keep trying to long as the number of pending
2909
# tiles is dropping:
2910
if last_terrain_report_pending == -1:
2911
last_terrain_report_pending = report.pending
2912
elif report.pending < last_terrain_report_pending:
2913
last_terrain_report_pending = report.pending
2914
tstart = now
2915
2916
self.delay_sim_time(1)
2917
2918
self.progress(self.dump_message_verbose(report))
2919
2920
expected_terrain_height = 583.5
2921
if abs(report.terrain_height - expected_terrain_height) > 0.5:
2922
raise NotAchievedException("Expected terrain height=%f got=%f" %
2923
(expected_terrain_height, report.terrain_height))
2924
2925
def TerrainLoiter(self):
2926
'''Test terrain following in loiter'''
2927
self.context_push()
2928
self.install_terrain_handlers_context()
2929
self.set_parameters({
2930
"TERRAIN_FOLLOW": 1, # enable terrain following in loiter
2931
"WP_LOITER_RAD": 2000, # set very large loiter rad to get some terrain changes
2932
})
2933
alt = 200
2934
self.takeoff(alt*0.9, alt*1.1)
2935
self.set_rc(3, 1500)
2936
self.change_mode("LOITER")
2937
self.progress("loitering at %um" % alt)
2938
tstart = self.get_sim_time()
2939
timeout = 60*15 # enough time to do one and a bit circles
2940
max_delta = 0
2941
while True:
2942
now = self.get_sim_time_cached()
2943
if now - tstart > timeout:
2944
break
2945
gpi = self.assert_receive_message('GLOBAL_POSITION_INT')
2946
terrain = self.assert_receive_message('TERRAIN_REPORT')
2947
rel_alt = terrain.current_height
2948
self.progress("%um above terrain (%um bove home)" %
2949
(rel_alt, gpi.relative_alt/1000.0))
2950
if rel_alt > alt*1.2 or rel_alt < alt * 0.8:
2951
raise NotAchievedException("Not terrain following")
2952
delta = abs(rel_alt - gpi.relative_alt/1000.0)
2953
if delta > max_delta:
2954
max_delta = delta
2955
want_max_delta = 30
2956
if max_delta < want_max_delta:
2957
raise NotAchievedException(
2958
"Expected terrain and home alts to vary more than they did (max=%u want=%u)" %
2959
(max_delta, want_max_delta))
2960
self.context_pop()
2961
self.progress("Returning home")
2962
self.fly_home_land_and_disarm(240)
2963
2964
def fly_external_AHRS(self, sim, eahrs_type, mission):
2965
"""Fly with external AHRS"""
2966
self.customise_SITL_commandline(["--serial4=sim:%s" % sim])
2967
2968
self.set_parameters({
2969
"EAHRS_TYPE": eahrs_type,
2970
"SERIAL4_PROTOCOL": 36,
2971
"SERIAL4_BAUD": 230400,
2972
"GPS1_TYPE": 21,
2973
"AHRS_EKF_TYPE": 11,
2974
"INS_GYR_CAL": 1,
2975
})
2976
self.reboot_sitl()
2977
self.delay_sim_time(5)
2978
self.progress("Running accelcal")
2979
self.run_cmd(
2980
mavutil.mavlink.MAV_CMD_PREFLIGHT_CALIBRATION,
2981
p5=4,
2982
timeout=5,
2983
)
2984
2985
self.wait_ready_to_arm()
2986
self.arm_vehicle()
2987
self.fly_mission(mission)
2988
2989
def wait_and_maintain_wind_estimate(
2990
self,
2991
want_speed,
2992
want_dir,
2993
timeout=10,
2994
speed_tolerance=0.5,
2995
dir_tolerance=5,
2996
**kwargs):
2997
'''wait for wind estimate to reach speed and direction'''
2998
2999
def validator(last, _min, _max):
3000
'''returns false of spd or direction is too-far wrong'''
3001
(spd, di) = last
3002
_min_spd, _min_dir = _min
3003
_max_spd, _max_dir = _max
3004
if spd < _min_spd or spd > _max_spd:
3005
return False
3006
# my apologies to whoever is staring at this and wondering
3007
# why we're not wrapping angles here...
3008
if di < _min_dir or di > _max_dir:
3009
return False
3010
return True
3011
3012
def value_getter():
3013
'''returns a tuple of (wind_speed, wind_dir), where wind_dir is 45 if
3014
wind is coming from NE'''
3015
m = self.assert_receive_message("WIND")
3016
return (m.speed, m.direction)
3017
3018
class ValueAverager(object):
3019
def __init__(self):
3020
self.speed_average = -1
3021
self.dir_average = -1
3022
self.count = 0.0
3023
3024
def add_value(self, value):
3025
(spd, di) = value
3026
if self.speed_average == -1:
3027
self.speed_average = spd
3028
self.dir_average = di
3029
else:
3030
self.speed_average += spd
3031
self.di_average += spd
3032
self.count += 1
3033
return (self.speed_average/self.count, self.dir_average/self.count)
3034
3035
def reset(self):
3036
self.count = 0
3037
self.speed_average = -1
3038
self.dir_average = -1
3039
3040
self.wait_and_maintain_range(
3041
value_name="WindEstimates",
3042
minimum=(want_speed-speed_tolerance, want_dir-dir_tolerance),
3043
maximum=(want_speed+speed_tolerance, want_dir+dir_tolerance),
3044
current_value_getter=value_getter,
3045
value_averager=ValueAverager(),
3046
validator=lambda last, _min, _max: validator(last, _min, _max),
3047
timeout=timeout,
3048
**kwargs
3049
)
3050
3051
def WindEstimates(self):
3052
'''fly non-external AHRS, ensure wind estimate correct'''
3053
self.set_parameters({
3054
"SIM_WIND_SPD": 5,
3055
"SIM_WIND_DIR": 45,
3056
})
3057
self.wait_ready_to_arm()
3058
self.takeoff(70) # default wind sim wind is a sqrt function up to 60m
3059
self.change_mode('LOITER')
3060
# use default estimator to determine when to check others:
3061
self.wait_and_maintain_wind_estimate(5, 45, timeout=120)
3062
3063
for ahrs_type in 0, 2, 3, 10:
3064
self.start_subtest("Checking AHRS_EKF_TYPE=%u" % ahrs_type)
3065
self.set_parameter("AHRS_EKF_TYPE", ahrs_type)
3066
self.wait_and_maintain_wind_estimate(
3067
5, 45,
3068
speed_tolerance=1,
3069
timeout=30
3070
)
3071
self.fly_home_land_and_disarm()
3072
3073
def VectorNavEAHRS(self):
3074
'''Test VectorNav EAHRS support'''
3075
self.fly_external_AHRS("VectorNav", 1, "ap1.txt")
3076
3077
def MicroStrainEAHRS5(self):
3078
'''Test MicroStrain EAHRS series 5 support'''
3079
self.fly_external_AHRS("MicroStrain5", 2, "ap1.txt")
3080
3081
def MicroStrainEAHRS7(self):
3082
'''Test MicroStrain EAHRS series 7 support'''
3083
self.fly_external_AHRS("MicroStrain7", 7, "ap1.txt")
3084
3085
def InertialLabsEAHRS(self):
3086
'''Test InertialLabs EAHRS support'''
3087
self.fly_external_AHRS("ILabs", 5, "ap1.txt")
3088
3089
def GpsSensorPreArmEAHRS(self):
3090
'''Test pre-arm checks related to EAHRS_SENSORS using the MicroStrain7 driver'''
3091
self.customise_SITL_commandline(["--serial4=sim:MicroStrain7"])
3092
3093
self.set_parameters({
3094
"EAHRS_TYPE": 7,
3095
"SERIAL4_PROTOCOL": 36,
3096
"SERIAL4_BAUD": 230400,
3097
"GPS1_TYPE": 0, # Disabled (simulate user setup error)
3098
"GPS2_TYPE": 0, # Disabled (simulate user setup error)
3099
"AHRS_EKF_TYPE": 11,
3100
"INS_GYR_CAL": 1,
3101
"EAHRS_SENSORS": 13, # GPS is enabled
3102
})
3103
self.reboot_sitl()
3104
self.delay_sim_time(5)
3105
self.progress("Running accelcal")
3106
self.run_cmd(
3107
mavutil.mavlink.MAV_CMD_PREFLIGHT_CALIBRATION,
3108
p5=4,
3109
timeout=5,
3110
)
3111
3112
self.assert_prearm_failure("ExternalAHRS: Incorrect number", # Cut short due to message limits.
3113
timeout=30,
3114
other_prearm_failures_fatal=False)
3115
3116
self.set_parameters({
3117
"EAHRS_TYPE": 7,
3118
"SERIAL4_PROTOCOL": 36,
3119
"SERIAL4_BAUD": 230400,
3120
"GPS1_TYPE": 1, # Auto
3121
"GPS2_TYPE": 21, # EARHS
3122
"AHRS_EKF_TYPE": 11,
3123
"INS_GYR_CAL": 1,
3124
"EAHRS_SENSORS": 13, # GPS is enabled
3125
})
3126
self.reboot_sitl()
3127
self.delay_sim_time(5)
3128
self.progress("Running accelcal")
3129
self.run_cmd(
3130
mavutil.mavlink.MAV_CMD_PREFLIGHT_CALIBRATION,
3131
p5=4,
3132
timeout=5,
3133
)
3134
# Check prearm success with MicroStrain when the first GPS is occupied by another GPS.
3135
# This supports the use case of comparing MicroStrain dual antenna to another GPS.
3136
self.wait_ready_to_arm()
3137
3138
def get_accelvec(self, m):
3139
return Vector3(m.xacc, m.yacc, m.zacc) * 0.001 * 9.81
3140
3141
def get_gyrovec(self, m):
3142
return Vector3(m.xgyro, m.ygyro, m.zgyro) * 0.001 * math.degrees(1)
3143
3144
def IMUTempCal(self):
3145
'''Test IMU temperature calibration'''
3146
self.progress("Setting up SITL temperature profile")
3147
self.set_parameters({
3148
"SIM_IMUT1_ENABLE" : 1,
3149
"SIM_IMUT1_ACC1_X" : 120000.000000,
3150
"SIM_IMUT1_ACC1_Y" : -190000.000000,
3151
"SIM_IMUT1_ACC1_Z" : 1493.864746,
3152
"SIM_IMUT1_ACC2_X" : -51.624416,
3153
"SIM_IMUT1_ACC2_Y" : 10.364172,
3154
"SIM_IMUT1_ACC2_Z" : -7878.000000,
3155
"SIM_IMUT1_ACC3_X" : -0.514242,
3156
"SIM_IMUT1_ACC3_Y" : 0.862218,
3157
"SIM_IMUT1_ACC3_Z" : -234.000000,
3158
"SIM_IMUT1_GYR1_X" : -5122.513817,
3159
"SIM_IMUT1_GYR1_Y" : -3250.470428,
3160
"SIM_IMUT1_GYR1_Z" : -2136.346676,
3161
"SIM_IMUT1_GYR2_X" : 30.720505,
3162
"SIM_IMUT1_GYR2_Y" : 17.778447,
3163
"SIM_IMUT1_GYR2_Z" : 0.765997,
3164
"SIM_IMUT1_GYR3_X" : -0.003572,
3165
"SIM_IMUT1_GYR3_Y" : 0.036346,
3166
"SIM_IMUT1_GYR3_Z" : 0.015457,
3167
"SIM_IMUT1_TMAX" : 70.0,
3168
"SIM_IMUT1_TMIN" : -20.000000,
3169
"SIM_IMUT2_ENABLE" : 1,
3170
"SIM_IMUT2_ACC1_X" : -160000.000000,
3171
"SIM_IMUT2_ACC1_Y" : 198730.000000,
3172
"SIM_IMUT2_ACC1_Z" : 27812.000000,
3173
"SIM_IMUT2_ACC2_X" : 30.658159,
3174
"SIM_IMUT2_ACC2_Y" : 32.085022,
3175
"SIM_IMUT2_ACC2_Z" : 1572.000000,
3176
"SIM_IMUT2_ACC3_X" : 0.102912,
3177
"SIM_IMUT2_ACC3_Y" : 0.229734,
3178
"SIM_IMUT2_ACC3_Z" : 172.000000,
3179
"SIM_IMUT2_GYR1_X" : 3173.925644,
3180
"SIM_IMUT2_GYR1_Y" : -2368.312836,
3181
"SIM_IMUT2_GYR1_Z" : -1796.497177,
3182
"SIM_IMUT2_GYR2_X" : 13.029696,
3183
"SIM_IMUT2_GYR2_Y" : -10.349280,
3184
"SIM_IMUT2_GYR2_Z" : -15.082653,
3185
"SIM_IMUT2_GYR3_X" : 0.004831,
3186
"SIM_IMUT2_GYR3_Y" : -0.020528,
3187
"SIM_IMUT2_GYR3_Z" : 0.009469,
3188
"SIM_IMUT2_TMAX" : 70.000000,
3189
"SIM_IMUT2_TMIN" : -20.000000,
3190
"SIM_IMUT_END" : 45.000000,
3191
"SIM_IMUT_START" : 3.000000,
3192
"SIM_IMUT_TCONST" : 75.000000,
3193
"SIM_DRIFT_SPEED" : 0,
3194
"INS_GYR_CAL" : 0,
3195
})
3196
3197
self.set_parameter("SIM_IMUT_FIXED", 12)
3198
self.progress("Running accel cal")
3199
self.run_cmd(
3200
mavutil.mavlink.MAV_CMD_PREFLIGHT_CALIBRATION,
3201
p5=4,
3202
timeout=5,
3203
)
3204
self.progress("Running gyro cal")
3205
self.run_cmd(
3206
mavutil.mavlink.MAV_CMD_PREFLIGHT_CALIBRATION,
3207
p5=1,
3208
timeout=5,
3209
)
3210
self.set_parameters({
3211
"SIM_IMUT_FIXED": 0,
3212
"INS_TCAL1_ENABLE": 2,
3213
"INS_TCAL1_TMAX": 42,
3214
"INS_TCAL2_ENABLE": 2,
3215
"INS_TCAL2_TMAX": 42,
3216
"SIM_SPEEDUP": 200,
3217
})
3218
self.set_parameter("LOG_DISARMED", 1)
3219
self.reboot_sitl()
3220
3221
self.progress("Waiting for IMU temperature")
3222
self.assert_reach_imu_temperature(43, timeout=600)
3223
3224
if self.get_parameter("INS_TCAL1_ENABLE") != 1.0:
3225
raise NotAchievedException("TCAL1 did not complete")
3226
if self.get_parameter("INS_TCAL2_ENABLE") != 1.0:
3227
raise NotAchievedException("TCAL2 did not complete")
3228
3229
self.progress("Logging with calibration enabled")
3230
self.reboot_sitl()
3231
3232
self.assert_reach_imu_temperature(43, timeout=600)
3233
3234
self.progress("Testing with compensation enabled")
3235
3236
test_temperatures = range(10, 45, 5)
3237
corrected = {}
3238
uncorrected = {}
3239
3240
for temp in test_temperatures:
3241
self.progress("Testing temperature %.1f" % temp)
3242
self.set_parameter("SIM_IMUT_FIXED", temp)
3243
self.delay_sim_time(2)
3244
for msg in ['RAW_IMU', 'SCALED_IMU2']:
3245
m = self.assert_receive_message(msg, timeout=2)
3246
temperature = m.temperature*0.01
3247
3248
if abs(temperature - temp) > 0.2:
3249
raise NotAchievedException("incorrect %s temperature %.1f should be %.1f" % (msg, temperature, temp))
3250
3251
accel = self.get_accelvec(m)
3252
gyro = self.get_gyrovec(m)
3253
accel2 = accel + Vector3(0, 0, 9.81)
3254
3255
corrected[temperature] = (accel2, gyro)
3256
3257
self.progress("Testing with compensation disabled")
3258
self.set_parameters({
3259
"INS_TCAL1_ENABLE": 0,
3260
"INS_TCAL2_ENABLE": 0,
3261
})
3262
3263
gyro_threshold = 0.2
3264
accel_threshold = 0.2
3265
3266
for temp in test_temperatures:
3267
self.progress("Testing temperature %.1f" % temp)
3268
self.set_parameter("SIM_IMUT_FIXED", temp)
3269
self.wait_heartbeat()
3270
self.wait_heartbeat()
3271
for msg in ['RAW_IMU', 'SCALED_IMU2']:
3272
m = self.assert_receive_message(msg, timeout=2)
3273
temperature = m.temperature*0.01
3274
3275
if abs(temperature - temp) > 0.2:
3276
raise NotAchievedException("incorrect %s temperature %.1f should be %.1f" % (msg, temperature, temp))
3277
3278
accel = self.get_accelvec(m)
3279
gyro = self.get_gyrovec(m)
3280
3281
accel2 = accel + Vector3(0, 0, 9.81)
3282
uncorrected[temperature] = (accel2, gyro)
3283
3284
for temp in test_temperatures:
3285
(accel, gyro) = corrected[temp]
3286
self.progress("Corrected gyro at %.1f %s" % (temp, gyro))
3287
self.progress("Corrected accel at %.1f %s" % (temp, accel))
3288
3289
for temp in test_temperatures:
3290
(accel, gyro) = uncorrected[temp]
3291
self.progress("Uncorrected gyro at %.1f %s" % (temp, gyro))
3292
self.progress("Uncorrected accel at %.1f %s" % (temp, accel))
3293
3294
bad_value = False
3295
for temp in test_temperatures:
3296
(accel, gyro) = corrected[temp]
3297
if gyro.length() > gyro_threshold:
3298
raise NotAchievedException("incorrect corrected at %.1f gyro %s" % (temp, gyro))
3299
3300
if accel.length() > accel_threshold:
3301
raise NotAchievedException("incorrect corrected at %.1f accel %s" % (temp, accel))
3302
3303
(accel, gyro) = uncorrected[temp]
3304
if gyro.length() > gyro_threshold*2:
3305
bad_value = True
3306
3307
if accel.length() > accel_threshold*2:
3308
bad_value = True
3309
3310
if not bad_value:
3311
raise NotAchievedException("uncompensated IMUs did not vary enough")
3312
3313
# the above tests change the internal persistent state of the
3314
# vehicle in ways that autotest doesn't track (magically set
3315
# parameters). So wipe the vehicle's eeprom:
3316
self.reset_SITL_commandline()
3317
3318
def EKFlaneswitch(self):
3319
'''Test EKF3 Affinity and Lane Switching'''
3320
3321
# new lane swtich available only with EK3
3322
self.set_parameters({
3323
"EK3_ENABLE": 1,
3324
"EK2_ENABLE": 0,
3325
"AHRS_EKF_TYPE": 3,
3326
"EK3_AFFINITY": 15, # enable affinity for all sensors
3327
"EK3_IMU_MASK": 3, # use only 2 IMUs
3328
"GPS2_TYPE": 1,
3329
"SIM_GPS2_ENABLE": 1,
3330
"SIM_BARO_COUNT": 2,
3331
"SIM_BAR2_DISABLE": 0,
3332
"ARSPD2_TYPE": 2,
3333
"ARSPD2_USE": 1,
3334
"ARSPD2_PIN": 2,
3335
})
3336
3337
# some parameters need reboot to take effect
3338
self.reboot_sitl()
3339
3340
self.lane_switches = []
3341
3342
# add an EKF lane switch hook
3343
def statustext_hook(mav, message):
3344
if message.get_type() != 'STATUSTEXT':
3345
return
3346
# example msg: EKF3 lane switch 1
3347
if not message.text.startswith("EKF3 lane switch "):
3348
return
3349
newlane = int(message.text[-1])
3350
self.lane_switches.append(newlane)
3351
self.install_message_hook_context(statustext_hook)
3352
3353
# get flying
3354
self.takeoff(alt=50)
3355
self.change_mode('CIRCLE')
3356
3357
###################################################################
3358
self.progress("Checking EKF3 Lane Switching trigger from all sensors")
3359
###################################################################
3360
self.start_subtest("ACCELEROMETER: Change z-axis offset")
3361
# create an accelerometer error by changing the Z-axis offset
3362
self.context_collect("STATUSTEXT")
3363
old_parameter = self.get_parameter("INS_ACCOFFS_Z")
3364
self.wait_statustext(
3365
text="EKF3 lane switch",
3366
timeout=30,
3367
the_function=self.set_parameter("INS_ACCOFFS_Z", old_parameter + 5),
3368
check_context=True)
3369
if self.lane_switches != [1]:
3370
raise NotAchievedException("Expected lane switch 1, got %s" % str(self.lane_switches[-1]))
3371
# Cleanup
3372
self.set_parameter("INS_ACCOFFS_Z", old_parameter)
3373
self.context_clear_collection("STATUSTEXT")
3374
self.wait_heading(0, accuracy=10, timeout=60)
3375
self.wait_heading(180, accuracy=10, timeout=60)
3376
###################################################################
3377
self.start_subtest("BAROMETER: Freeze to last measured value")
3378
self.context_collect("STATUSTEXT")
3379
# create a barometer error by inhibiting any pressure change while changing altitude
3380
old_parameter = self.get_parameter("SIM_BAR2_FREEZE")
3381
self.set_parameter("SIM_BAR2_FREEZE", 1)
3382
self.wait_statustext(
3383
text="EKF3 lane switch",
3384
timeout=30,
3385
the_function=lambda: self.set_rc(2, 2000),
3386
check_context=True)
3387
if self.lane_switches != [1, 0]:
3388
raise NotAchievedException("Expected lane switch 0, got %s" % str(self.lane_switches[-1]))
3389
# Cleanup
3390
self.set_rc(2, 1500)
3391
self.set_parameter("SIM_BAR2_FREEZE", old_parameter)
3392
self.context_clear_collection("STATUSTEXT")
3393
self.wait_heading(0, accuracy=10, timeout=60)
3394
self.wait_heading(180, accuracy=10, timeout=60)
3395
###################################################################
3396
self.start_subtest("GPS: Apply GPS Velocity Error in NED")
3397
self.context_push()
3398
self.context_collect("STATUSTEXT")
3399
3400
# create a GPS velocity error by adding a random 2m/s
3401
# noise on each axis
3402
def sim_gps_verr():
3403
self.set_parameters({
3404
"SIM_GPS1_VERR_X": self.get_parameter("SIM_GPS1_VERR_X") + 2,
3405
"SIM_GPS1_VERR_Y": self.get_parameter("SIM_GPS1_VERR_Y") + 2,
3406
"SIM_GPS1_VERR_Z": self.get_parameter("SIM_GPS1_VERR_Z") + 2,
3407
})
3408
self.wait_statustext(text="EKF3 lane switch", timeout=30, the_function=sim_gps_verr, check_context=True)
3409
if self.lane_switches != [1, 0, 1]:
3410
raise NotAchievedException("Expected lane switch 1, got %s" % str(self.lane_switches[-1]))
3411
# Cleanup
3412
self.context_pop()
3413
self.context_clear_collection("STATUSTEXT")
3414
self.wait_heading(0, accuracy=10, timeout=60)
3415
self.wait_heading(180, accuracy=10, timeout=60)
3416
###################################################################
3417
self.start_subtest("MAGNETOMETER: Change X-Axis Offset")
3418
self.context_collect("STATUSTEXT")
3419
# create a magnetometer error by changing the X-axis offset
3420
old_parameter = self.get_parameter("SIM_MAG2_OFS_X")
3421
self.wait_statustext(
3422
text="EKF3 lane switch",
3423
timeout=30,
3424
the_function=self.set_parameter("SIM_MAG2_OFS_X", old_parameter + 150),
3425
check_context=True)
3426
if self.lane_switches != [1, 0, 1, 0]:
3427
raise NotAchievedException("Expected lane switch 0, got %s" % str(self.lane_switches[-1]))
3428
# Cleanup
3429
self.set_parameter("SIM_MAG2_OFS_X", old_parameter)
3430
self.context_clear_collection("STATUSTEXT")
3431
self.wait_heading(0, accuracy=10, timeout=60)
3432
self.wait_heading(180, accuracy=10, timeout=60)
3433
###################################################################
3434
self.start_subtest("AIRSPEED: Fail to constant value")
3435
self.context_push()
3436
self.context_collect("STATUSTEXT")
3437
3438
old_parameter = self.get_parameter("SIM_ARSPD_FAIL")
3439
3440
def fail_speed():
3441
self.change_mode("GUIDED")
3442
loc = self.mav.location()
3443
self.run_cmd_int(
3444
mavutil.mavlink.MAV_CMD_DO_REPOSITION,
3445
p5=int(loc.lat * 1e7),
3446
p6=int(loc.lng * 1e7),
3447
p7=50, # alt
3448
frame=mavutil.mavlink.MAV_FRAME_GLOBAL_RELATIVE_ALT,
3449
)
3450
self.delay_sim_time(5)
3451
# create an airspeed sensor error by freezing to the
3452
# current airspeed then changing the airspeed demand
3453
# to a higher value and waiting for the TECS speed
3454
# loop to diverge
3455
m = self.mav.recv_match(type='VFR_HUD', blocking=True)
3456
self.set_parameter("SIM_ARSPD_FAIL", m.airspeed)
3457
self.run_cmd(
3458
mavutil.mavlink.MAV_CMD_DO_CHANGE_SPEED,
3459
p1=0, # airspeed
3460
p2=30,
3461
p3=-1, # throttle / no change
3462
p4=0, # absolute values
3463
)
3464
self.wait_statustext(text="EKF3 lane switch", timeout=30, the_function=fail_speed, check_context=True)
3465
if self.lane_switches != [1, 0, 1, 0, 1]:
3466
raise NotAchievedException("Expected lane switch 1, got %s" % str(self.lane_switches[-1]))
3467
# Cleanup
3468
self.set_parameter("SIM_ARSPD_FAIL", old_parameter)
3469
self.change_mode('CIRCLE')
3470
self.context_pop()
3471
self.context_clear_collection("STATUSTEXT")
3472
self.wait_heading(0, accuracy=10, timeout=60)
3473
self.wait_heading(180, accuracy=10, timeout=60)
3474
###################################################################
3475
self.progress("GYROSCOPE: Change Y-Axis Offset")
3476
self.context_collect("STATUSTEXT")
3477
# create a gyroscope error by changing the Y-axis offset
3478
old_parameter = self.get_parameter("INS_GYR2OFFS_Y")
3479
self.wait_statustext(
3480
text="EKF3 lane switch",
3481
timeout=30,
3482
the_function=self.set_parameter("INS_GYR2OFFS_Y", old_parameter + 1),
3483
check_context=True)
3484
if self.lane_switches != [1, 0, 1, 0, 1, 0]:
3485
raise NotAchievedException("Expected lane switch 0, got %s" % str(self.lane_switches[-1]))
3486
# Cleanup
3487
self.set_parameter("INS_GYR2OFFS_Y", old_parameter)
3488
self.context_clear_collection("STATUSTEXT")
3489
###################################################################
3490
3491
self.disarm_vehicle(force=True)
3492
3493
def FenceAltCeilFloor(self):
3494
'''Tests the fence ceiling and floor'''
3495
self.set_parameters({
3496
"FENCE_TYPE": 9, # Set fence type to max and min alt
3497
"FENCE_ACTION": 0, # Set action to report
3498
"FENCE_ALT_MAX": 200,
3499
"FENCE_ALT_MIN": 100,
3500
})
3501
3502
# Grab Home Position
3503
self.wait_ready_to_arm()
3504
startpos = self.mav.location()
3505
3506
cruise_alt = 150
3507
self.takeoff(cruise_alt)
3508
3509
# note that while we enable the fence here, since the action
3510
# is set to report-only the fence continues to show as
3511
# not-enabled in the assert calls below
3512
self.do_fence_enable()
3513
3514
self.progress("Fly above ceiling and check for breach")
3515
self.change_altitude(startpos.alt + cruise_alt + 80)
3516
self.assert_fence_sys_status(True, False, False)
3517
3518
self.progress("Return to cruise alt")
3519
self.change_altitude(startpos.alt + cruise_alt)
3520
3521
self.progress("Ensure breach has clearned")
3522
self.assert_fence_sys_status(True, False, True)
3523
3524
self.progress("Fly below floor and check for breach")
3525
self.change_altitude(startpos.alt + cruise_alt - 80)
3526
3527
self.progress("Ensure breach has clearned")
3528
self.assert_fence_sys_status(True, False, False)
3529
3530
self.do_fence_disable()
3531
3532
self.fly_home_land_and_disarm(timeout=150)
3533
3534
def FenceMinAltAutoEnable(self):
3535
'''Tests autoenablement of the alt min fence and fences on arming'''
3536
self.set_parameters({
3537
"FENCE_TYPE": 9, # Set fence type to min alt and max alt
3538
"FENCE_ACTION": 1, # Set action to RTL
3539
"FENCE_ALT_MIN": 25,
3540
"FENCE_ALT_MAX": 100,
3541
"FENCE_AUTOENABLE": 3,
3542
"FENCE_ENABLE" : 0,
3543
"RTL_AUTOLAND" : 2,
3544
})
3545
3546
# check we can takeoff again
3547
for i in [1, 2]:
3548
# Grab Home Position
3549
self.wait_ready_to_arm()
3550
self.arm_vehicle()
3551
# max alt fence should now be enabled
3552
if i == 1:
3553
self.assert_fence_enabled()
3554
3555
self.takeoff(alt=50, mode='TAKEOFF')
3556
self.change_mode("FBWA")
3557
self.set_rc(3, 1100) # lower throttle
3558
3559
self.progress("Waiting for RTL")
3560
tstart = self.get_sim_time()
3561
mode = "RTL"
3562
while not self.mode_is(mode, drain_mav=False):
3563
self.mav.messages['HEARTBEAT'].custom_mode
3564
self.progress("mav.flightmode=%s Want=%s Alt=%f" % (
3565
self.mav.flightmode, mode, self.get_altitude(relative=True)))
3566
if (self.get_sim_time_cached() > tstart + 120):
3567
raise WaitModeTimeout("Did not change mode")
3568
self.progress("Got mode %s" % mode)
3569
self.fly_home_land_and_disarm()
3570
self.change_mode("FBWA")
3571
self.clear_mission(mavutil.mavlink.MAV_MISSION_TYPE_ALL)
3572
self.set_current_waypoint(0, check_afterwards=False)
3573
self.set_rc(3, 1000) # lower throttle
3574
3575
def FenceMinAltEnableAutoland(self):
3576
'''Tests autolanding when alt min fence is enabled'''
3577
self.set_parameters({
3578
"FENCE_TYPE": 12, # Set fence type to min alt and max alt
3579
"FENCE_ACTION": 1, # Set action to RTL
3580
"FENCE_ALT_MIN": 20,
3581
"FENCE_AUTOENABLE": 0,
3582
"FENCE_ENABLE" : 1,
3583
"RTL_AUTOLAND" : 2,
3584
})
3585
3586
# Grab Home Position
3587
self.wait_ready_to_arm()
3588
self.arm_vehicle()
3589
3590
self.takeoff(alt=50, mode='TAKEOFF')
3591
self.change_mode("FBWA")
3592
self.set_rc(3, 1100) # lower throttle
3593
3594
self.progress("Waiting for RTL")
3595
tstart = self.get_sim_time()
3596
mode = "RTL"
3597
while not self.mode_is(mode, drain_mav=False):
3598
self.mav.messages['HEARTBEAT'].custom_mode
3599
self.progress("mav.flightmode=%s Want=%s Alt=%f" % (
3600
self.mav.flightmode, mode, self.get_altitude(relative=True)))
3601
if (self.get_sim_time_cached() > tstart + 120):
3602
raise WaitModeTimeout("Did not change mode")
3603
self.progress("Got mode %s" % mode)
3604
# switch to FBWA
3605
self.change_mode("FBWA")
3606
self.set_rc(3, 1500) # raise throttle
3607
self.wait_altitude(25, 35, timeout=50, relative=True)
3608
self.set_rc(3, 1000) # lower throttle
3609
# Now check we can land
3610
self.fly_home_land_and_disarm()
3611
3612
def FenceMinAltAutoEnableAbort(self):
3613
'''Tests autoenablement of the alt min fence and fences on arming'''
3614
self.set_parameters({
3615
"FENCE_TYPE": 8, # Set fence type to min alt
3616
"FENCE_ACTION": 1, # Set action to RTL
3617
"FENCE_ALT_MIN": 25,
3618
"FENCE_ALT_MAX": 100,
3619
"FENCE_AUTOENABLE": 3,
3620
"FENCE_ENABLE" : 0,
3621
"RTL_AUTOLAND" : 2,
3622
})
3623
3624
self.wait_ready_to_arm()
3625
self.arm_vehicle()
3626
3627
self.takeoff(alt=50, mode='TAKEOFF')
3628
self.change_mode("FBWA")
3629
# min alt fence should now be enabled
3630
self.assert_fence_enabled()
3631
self.set_rc(3, 1100) # lower throttle
3632
3633
self.progress("Waiting for RTL")
3634
tstart = self.get_sim_time()
3635
mode = "RTL"
3636
while not self.mode_is(mode, drain_mav=False):
3637
self.mav.messages['HEARTBEAT'].custom_mode
3638
self.progress("mav.flightmode=%s Want=%s Alt=%f" % (
3639
self.mav.flightmode, mode, self.get_altitude(relative=True)))
3640
if (self.get_sim_time_cached() > tstart + 120):
3641
raise WaitModeTimeout("Did not change mode")
3642
self.progress("Got mode %s" % mode)
3643
3644
self.load_generic_mission("flaps.txt")
3645
self.change_mode("AUTO")
3646
self.wait_distance_to_waypoint(8, 100, 10000000)
3647
self.set_current_waypoint(8)
3648
# abort the landing
3649
self.wait_altitude(10, 20, timeout=200, relative=True)
3650
self.change_mode("CRUISE")
3651
self.set_rc(2, 1200)
3652
# self.set_rc(3, 1600) # raise throttle
3653
self.wait_altitude(30, 40, timeout=200, relative=True)
3654
# min alt fence should now be re-enabled
3655
self.assert_fence_enabled()
3656
3657
self.change_mode("AUTO")
3658
self.clear_mission(mavutil.mavlink.MAV_MISSION_TYPE_ALL)
3659
self.fly_home_land_and_disarm(timeout=150)
3660
3661
def FenceAutoEnableDisableSwitch(self):
3662
'''Tests autoenablement of regular fences and manual disablement'''
3663
self.set_parameters({
3664
"FENCE_TYPE": 9, # Set fence type to min alt, max alt
3665
"FENCE_ACTION": 1, # Set action to RTL
3666
"FENCE_ALT_MIN": 50,
3667
"FENCE_ALT_MAX": 100,
3668
"FENCE_AUTOENABLE": 2,
3669
"FENCE_OPTIONS" : 1,
3670
"FENCE_ENABLE" : 1,
3671
"FENCE_RADIUS" : 300,
3672
"FENCE_RET_ALT" : 0,
3673
"FENCE_RET_RALLY" : 0,
3674
"FENCE_TOTAL" : 0,
3675
"RTL_ALTITUDE" : 75,
3676
"TKOFF_ALT" : 75,
3677
"RC7_OPTION" : 11, # AC_Fence uses Aux switch functionality
3678
})
3679
self.reboot_sitl()
3680
self.context_collect("STATUSTEXT")
3681
3682
fence_bit = mavutil.mavlink.MAV_SYS_STATUS_GEOFENCE
3683
# Grab Home Position
3684
self.mav.recv_match(type='HOME_POSITION', blocking=True)
3685
self.set_rc(7, 1000) # Turn fence off with aux function, does not impact later auto-enable
3686
3687
self.wait_ready_to_arm()
3688
3689
self.progress("Check fence disabled at boot")
3690
m = self.mav.recv_match(type='SYS_STATUS', blocking=True)
3691
if (m.onboard_control_sensors_enabled & fence_bit):
3692
raise NotAchievedException("Fence is enabled at boot")
3693
3694
cruise_alt = 75
3695
self.takeoff(cruise_alt, mode='TAKEOFF')
3696
3697
self.progress("Fly above ceiling and check there is a breach")
3698
self.change_mode('FBWA')
3699
self.set_rc(3, 2000)
3700
self.set_rc(2, 1000)
3701
3702
self.wait_statustext("Max Alt fence breached", timeout=10, check_context=True)
3703
self.wait_mode('RTL')
3704
3705
m = self.mav.recv_match(type='SYS_STATUS', blocking=True)
3706
if (m.onboard_control_sensors_health & fence_bit):
3707
raise NotAchievedException("Fence ceiling not breached")
3708
3709
self.set_rc(3, 1500)
3710
self.set_rc(2, 1500)
3711
3712
self.progress("Wait for RTL alt reached")
3713
self.wait_altitude(cruise_alt-5, cruise_alt+5, relative=True, timeout=30)
3714
3715
self.progress("Return to cruise alt")
3716
self.set_rc(3, 1500)
3717
self.change_altitude(cruise_alt, relative=True)
3718
3719
self.progress("Check fence breach cleared")
3720
m = self.mav.recv_match(type='SYS_STATUS', blocking=True)
3721
if (not (m.onboard_control_sensors_health & fence_bit)):
3722
raise NotAchievedException("Fence breach not cleared")
3723
3724
self.progress("Fly below floor and check for breach")
3725
self.set_rc(2, 2000)
3726
self.wait_statustext("Min Alt fence breached", timeout=10, check_context=True)
3727
self.wait_mode("RTL")
3728
m = self.mav.recv_match(type='SYS_STATUS', blocking=True)
3729
if (m.onboard_control_sensors_health & fence_bit):
3730
raise NotAchievedException("Fence floor not breached")
3731
3732
self.change_mode("FBWA")
3733
3734
self.progress("Fly above floor and check fence is enabled")
3735
self.set_rc(3, 2000)
3736
self.change_altitude(75, relative=True)
3737
m = self.mav.recv_match(type='SYS_STATUS', blocking=True)
3738
if (not (m.onboard_control_sensors_enabled & fence_bit)):
3739
raise NotAchievedException("Fence Floor not enabled")
3740
3741
self.progress("Toggle fence enable/disable")
3742
self.set_rc(7, 2000)
3743
self.delay_sim_time(2)
3744
self.set_rc(7, 1000)
3745
self.delay_sim_time(2)
3746
3747
self.progress("Check fence is disabled")
3748
m = self.mav.recv_match(type='SYS_STATUS', blocking=True)
3749
if (m.onboard_control_sensors_enabled & fence_bit):
3750
raise NotAchievedException("Fence disable with switch failed")
3751
3752
self.progress("Fly below floor and check for no breach")
3753
self.change_altitude(40, relative=True)
3754
m = self.mav.recv_match(type='SYS_STATUS', blocking=True)
3755
if (not (m.onboard_control_sensors_health & fence_bit)):
3756
raise NotAchievedException("Fence floor breached")
3757
3758
self.progress("Return to cruise alt")
3759
self.set_rc(3, 1500)
3760
self.change_altitude(cruise_alt, relative=True)
3761
self.fly_home_land_and_disarm(timeout=250)
3762
3763
def FenceCircleExclusionAutoEnable(self):
3764
'''Tests autolanding when alt min fence is enabled'''
3765
self.set_parameters({
3766
"FENCE_TYPE": 2, # Set fence type to circle
3767
"FENCE_ACTION": 1, # Set action to RTL
3768
"FENCE_AUTOENABLE": 2,
3769
"FENCE_ENABLE" : 0,
3770
"RTL_AUTOLAND" : 2,
3771
})
3772
3773
fence_loc = self.home_position_as_mav_location()
3774
self.location_offset_ne(fence_loc, 300, 0)
3775
3776
self.upload_fences_from_locations([(
3777
mavutil.mavlink.MAV_CMD_NAV_FENCE_CIRCLE_EXCLUSION, {
3778
"radius" : 100,
3779
"loc" : fence_loc
3780
}
3781
)])
3782
3783
self.takeoff(alt=50, mode='TAKEOFF')
3784
self.change_mode("FBWA")
3785
self.set_rc(3, 1100) # lower throttle
3786
3787
self.progress("Waiting for RTL")
3788
self.wait_mode('RTL')
3789
# Now check we can land
3790
self.fly_home_land_and_disarm()
3791
3792
def FenceEnableDisableSwitch(self):
3793
'''Tests enablement and disablement of fences on a switch'''
3794
fence_bit = mavutil.mavlink.MAV_SYS_STATUS_GEOFENCE
3795
3796
self.set_parameters({
3797
"FENCE_TYPE": 4, # Set fence type to polyfence
3798
"FENCE_ACTION": 6, # Set action to GUIDED
3799
"FENCE_ALT_MIN": 10,
3800
"FENCE_ENABLE" : 0,
3801
"RC7_OPTION" : 11, # AC_Fence uses Aux switch functionality
3802
})
3803
3804
self.progress("Checking fence is not present before being configured")
3805
m = self.mav.recv_match(type='SYS_STATUS', blocking=True)
3806
self.progress("Got (%s)" % str(m))
3807
if (m.onboard_control_sensors_enabled & fence_bit):
3808
raise NotAchievedException("Fence enabled before being configured")
3809
3810
self.wait_ready_to_arm()
3811
# takeoff at a lower altitude to avoid immediately breaching polyfence
3812
self.takeoff(alt=25)
3813
self.change_mode("FBWA")
3814
3815
self.load_fence("CMAC-fence.txt")
3816
3817
self.set_rc_from_map({
3818
3: 1500,
3819
7: 2000,
3820
}) # Turn fence on with aux function
3821
3822
m = self.mav.recv_match(type='FENCE_STATUS', blocking=True, timeout=2)
3823
self.progress("Got (%s)" % str(m))
3824
if m is None:
3825
raise NotAchievedException("Got FENCE_STATUS unexpectedly")
3826
3827
self.progress("Checking fence is initially OK")
3828
self.wait_sensor_state(mavutil.mavlink.MAV_SYS_STATUS_GEOFENCE,
3829
present=True,
3830
enabled=True,
3831
healthy=True,
3832
verbose=False,
3833
timeout=30)
3834
3835
self.progress("Waiting for GUIDED")
3836
tstart = self.get_sim_time()
3837
mode = "GUIDED"
3838
while not self.mode_is(mode, drain_mav=False):
3839
self.mav.messages['HEARTBEAT'].custom_mode
3840
self.progress("mav.flightmode=%s Want=%s Alt=%f" % (
3841
self.mav.flightmode, mode, self.get_altitude(relative=True)))
3842
if (self.get_sim_time_cached() > tstart + 120):
3843
raise WaitModeTimeout("Did not change mode")
3844
self.progress("Got mode %s" % mode)
3845
# check we are in breach
3846
self.assert_fence_enabled()
3847
3848
self.set_rc_from_map({
3849
7: 1000,
3850
}) # Turn fence off with aux function
3851
3852
# wait to no longer be in breach
3853
self.delay_sim_time(5)
3854
self.assert_fence_disabled()
3855
3856
self.fly_home_land_and_disarm(timeout=250)
3857
self.do_fence_disable() # Ensure the fence is disabled after test
3858
3859
def FenceEnableDisableAux(self):
3860
'''Tests enablement and disablement of fences via aux command'''
3861
fence_bit = mavutil.mavlink.MAV_SYS_STATUS_GEOFENCE
3862
3863
enable = 0
3864
self.set_parameters({
3865
"FENCE_TYPE": 12, # Set fence type to polyfence + AltMin
3866
"FENCE_ALT_MIN": 10,
3867
"FENCE_ENABLE" : enable,
3868
})
3869
3870
if not enable:
3871
self.progress("Checking fence is not present before being configured")
3872
m = self.mav.recv_match(type='SYS_STATUS', blocking=True)
3873
self.progress("Got (%s)" % str(m))
3874
if (m.onboard_control_sensors_enabled & fence_bit):
3875
raise NotAchievedException("Fence enabled before being configured")
3876
3877
self.load_fence("CMAC-fence.txt")
3878
3879
self.wait_ready_to_arm()
3880
# takeoff at a lower altitude to avoid immediately breaching polyfence
3881
self.takeoff(alt=25)
3882
self.change_mode("CRUISE")
3883
self.wait_distance(150, accuracy=20)
3884
3885
self.run_auxfunc(
3886
11,
3887
2,
3888
want_result=mavutil.mavlink.MAV_RESULT_ACCEPTED
3889
)
3890
3891
m = self.mav.recv_match(type='FENCE_STATUS', blocking=True, timeout=2)
3892
self.progress("Got (%s)" % str(m))
3893
if m is None:
3894
raise NotAchievedException("Got FENCE_STATUS unexpectedly")
3895
3896
self.progress("Checking fence is initially OK")
3897
self.wait_sensor_state(mavutil.mavlink.MAV_SYS_STATUS_GEOFENCE,
3898
present=True,
3899
enabled=True,
3900
healthy=True,
3901
verbose=False,
3902
timeout=30)
3903
3904
self.progress("Waiting for RTL")
3905
tstart = self.get_sim_time()
3906
mode = "RTL"
3907
while not self.mode_is(mode, drain_mav=False):
3908
self.mav.messages['HEARTBEAT'].custom_mode
3909
self.progress("mav.flightmode=%s Want=%s Alt=%f" % (
3910
self.mav.flightmode, mode, self.get_altitude(relative=True)))
3911
if (self.get_sim_time_cached() > tstart + 120):
3912
raise WaitModeTimeout("Did not change mode")
3913
self.progress("Got mode %s" % mode)
3914
# check we are in breach
3915
self.assert_fence_enabled()
3916
self.assert_fence_sys_status(True, True, False)
3917
3918
# wait until we get home
3919
self.wait_distance_to_home(50, 100, timeout=200)
3920
# now check we are now not in breach
3921
self.assert_fence_sys_status(True, True, True)
3922
# Turn fence off with aux function
3923
self.run_auxfunc(
3924
11,
3925
0,
3926
want_result=mavutil.mavlink.MAV_RESULT_ACCEPTED
3927
)
3928
# switch back to cruise
3929
self.change_mode("CRUISE")
3930
self.wait_distance(150, accuracy=20)
3931
3932
# re-enable the fences
3933
self.run_auxfunc(
3934
11,
3935
2,
3936
want_result=mavutil.mavlink.MAV_RESULT_ACCEPTED
3937
)
3938
3939
m = self.mav.recv_match(type='FENCE_STATUS', blocking=True, timeout=2)
3940
self.progress("Got (%s)" % str(m))
3941
if m is None:
3942
raise NotAchievedException("Got FENCE_STATUS unexpectedly")
3943
3944
self.progress("Waiting for RTL")
3945
tstart = self.get_sim_time()
3946
mode = "RTL"
3947
while not self.mode_is(mode, drain_mav=False):
3948
self.mav.messages['HEARTBEAT'].custom_mode
3949
self.progress("mav.flightmode=%s Want=%s Alt=%f" % (
3950
self.mav.flightmode, mode, self.get_altitude(relative=True)))
3951
if (self.get_sim_time_cached() > tstart + 120):
3952
raise WaitModeTimeout("Did not change mode")
3953
self.progress("Got mode %s" % mode)
3954
3955
# wait to no longer be in breach
3956
self.wait_distance_to_home(50, 100, timeout=200)
3957
self.assert_fence_sys_status(True, True, True)
3958
3959
# fly home and land with fences still enabled
3960
self.fly_home_land_and_disarm(timeout=250)
3961
self.do_fence_disable() # Ensure the fence is disabled after test
3962
3963
def FenceBreachedChangeMode(self):
3964
'''Tests manual mode change after fence breach, as set with FENCE_OPTIONS'''
3965
""" Attempts to change mode while a fence is breached.
3966
mode should change should fail if fence option bit is set"""
3967
self.set_parameters({
3968
"FENCE_ACTION": 1,
3969
"FENCE_TYPE": 4,
3970
})
3971
home_loc = self.mav.location()
3972
locs = [
3973
mavutil.location(home_loc.lat - 0.001, home_loc.lng - 0.001, 0, 0),
3974
mavutil.location(home_loc.lat - 0.001, home_loc.lng + 0.001, 0, 0),
3975
mavutil.location(home_loc.lat + 0.001, home_loc.lng + 0.001, 0, 0),
3976
mavutil.location(home_loc.lat + 0.001, home_loc.lng - 0.001, 0, 0),
3977
]
3978
self.upload_fences_from_locations([
3979
(mavutil.mavlink.MAV_CMD_NAV_FENCE_POLYGON_VERTEX_INCLUSION, locs),
3980
])
3981
self.delay_sim_time(1)
3982
self.wait_ready_to_arm()
3983
self.takeoff(alt=50)
3984
self.change_mode("CRUISE")
3985
self.wait_distance(250, accuracy=15)
3986
3987
self.progress("Enable fence and initiate fence action")
3988
self.do_fence_enable()
3989
self.assert_fence_enabled()
3990
self.wait_mode("RTL") # We should RTL because of fence breach
3991
3992
self.progress("User mode change to cruise should retrigger fence action")
3993
try:
3994
# mode change should time out, 'WaitModeTimeout' exception is the desired resut
3995
# cant wait too long or the vehicle will be inside fence and allow the mode change
3996
self.change_mode("CRUISE", timeout=10)
3997
raise NotAchievedException("Should not change mode in fence breach")
3998
except WaitModeTimeout:
3999
pass
4000
except Exception as e:
4001
raise e
4002
4003
# enable mode change
4004
self.set_parameter("FENCE_OPTIONS", 0)
4005
self.progress("Check user mode change to LOITER is allowed")
4006
self.change_mode("LOITER")
4007
4008
# Fly for 20 seconds and make sure still in LOITER mode
4009
self.delay_sim_time(20)
4010
if not self.mode_is("LOITER"):
4011
raise NotAchievedException("Fence should not re-trigger")
4012
4013
# reset options parameter
4014
self.set_parameter("FENCE_OPTIONS", 1)
4015
4016
self.progress("Test complete, disable fence and come home")
4017
self.do_fence_disable()
4018
self.fly_home_land_and_disarm()
4019
4020
def FenceNoFenceReturnPoint(self):
4021
'''Tests calculated return point during fence breach when no fence return point present'''
4022
""" Attempts to change mode while a fence is breached.
4023
This should revert to the mode specified by the fence action. """
4024
want_radius = 100 # Fence Return Radius
4025
self.set_parameters({
4026
"FENCE_ACTION": 6,
4027
"FENCE_TYPE": 4,
4028
"RTL_RADIUS": want_radius,
4029
"NAVL1_LIM_BANK": 60,
4030
})
4031
home_loc = self.mav.location()
4032
locs = [
4033
mavutil.location(home_loc.lat - 0.003, home_loc.lng - 0.001, 0, 0),
4034
mavutil.location(home_loc.lat - 0.003, home_loc.lng + 0.003, 0, 0),
4035
mavutil.location(home_loc.lat + 0.001, home_loc.lng + 0.003, 0, 0),
4036
mavutil.location(home_loc.lat + 0.001, home_loc.lng - 0.001, 0, 0),
4037
]
4038
self.upload_fences_from_locations([
4039
(mavutil.mavlink.MAV_CMD_NAV_FENCE_POLYGON_VERTEX_INCLUSION, locs),
4040
])
4041
self.delay_sim_time(1)
4042
self.wait_ready_to_arm()
4043
self.takeoff(alt=50)
4044
self.change_mode("CRUISE")
4045
self.wait_distance(150, accuracy=20)
4046
4047
self.progress("Enable fence and initiate fence action")
4048
self.do_fence_enable()
4049
self.assert_fence_enabled()
4050
self.wait_mode("GUIDED", timeout=120) # We should RTL because of fence breach
4051
self.delay_sim_time(60)
4052
4053
items = self.download_using_mission_protocol(mavutil.mavlink.MAV_MISSION_TYPE_FENCE)
4054
if len(items) != 4:
4055
raise NotAchievedException("Unexpected fencepoint count (want=%u got=%u)" % (4, len(items)))
4056
4057
# Check there are no fence return points specified still
4058
for fence_loc in items:
4059
if fence_loc.command == mavutil.mavlink.MAV_CMD_NAV_FENCE_RETURN_POINT:
4060
raise NotAchievedException(
4061
"Unexpected fence return point found (%u) got %u" %
4062
(fence_loc.command,
4063
mavutil.mavlink.MAV_CMD_NAV_FENCE_RETURN_POINT))
4064
4065
# Work out the approximate return point when no fence return point present
4066
# Logic taken from AC_PolyFence_loader.cpp
4067
min_loc = self.mav.location()
4068
max_loc = self.mav.location()
4069
for new_loc in locs:
4070
if new_loc.lat < min_loc.lat:
4071
min_loc.lat = new_loc.lat
4072
if new_loc.lng < min_loc.lng:
4073
min_loc.lng = new_loc.lng
4074
if new_loc.lat > max_loc.lat:
4075
max_loc.lat = new_loc.lat
4076
if new_loc.lng > max_loc.lng:
4077
max_loc.lng = new_loc.lng
4078
4079
# Generate the return location based on min and max locs
4080
ret_lat = (min_loc.lat + max_loc.lat) / 2
4081
ret_lng = (min_loc.lng + max_loc.lng) / 2
4082
ret_loc = mavutil.location(ret_lat, ret_lng, 0, 0)
4083
self.progress("Return loc: (%s)" % str(ret_loc))
4084
4085
# Wait for guided return to vehicle calculated fence return location
4086
self.wait_distance_to_location(ret_loc, 90, 110)
4087
self.wait_circling_point_with_radius(ret_loc, 92)
4088
4089
self.progress("Test complete, disable fence and come home")
4090
self.do_fence_disable()
4091
self.fly_home_land_and_disarm()
4092
4093
def FenceNoFenceReturnPointInclusion(self):
4094
'''Tests using home as fence return point when none is present, and no inclusion fence is uploaded'''
4095
""" Test result when a breach occurs and No fence return point is present and
4096
no inclusion fence is present and exclusion fence is present """
4097
want_radius = 100 # Fence Return Radius
4098
4099
self.set_parameters({
4100
"FENCE_ACTION": 6,
4101
"FENCE_TYPE": 2,
4102
"FENCE_RADIUS": 300,
4103
"RTL_RADIUS": want_radius,
4104
"NAVL1_LIM_BANK": 60,
4105
})
4106
4107
self.clear_fence()
4108
4109
self.delay_sim_time(1)
4110
self.wait_ready_to_arm()
4111
home_loc = self.mav.location()
4112
self.takeoff(alt=50)
4113
self.change_mode("CRUISE")
4114
self.wait_distance(150, accuracy=20)
4115
4116
self.progress("Enable fence and initiate fence action")
4117
self.do_fence_enable()
4118
self.assert_fence_enabled()
4119
self.wait_mode("GUIDED") # We should RTL because of fence breach
4120
self.delay_sim_time(30)
4121
4122
items = self.download_using_mission_protocol(mavutil.mavlink.MAV_MISSION_TYPE_FENCE)
4123
if len(items) != 0:
4124
raise NotAchievedException("Unexpected fencepoint count (want=%u got=%u)" % (0, len(items)))
4125
4126
# Check there are no fence return points specified still
4127
for fence_loc in items:
4128
if fence_loc.command == mavutil.mavlink.MAV_CMD_NAV_FENCE_RETURN_POINT:
4129
raise NotAchievedException(
4130
"Unexpected fence return point found (%u) got %u" %
4131
(fence_loc.command,
4132
mavutil.mavlink.MAV_CMD_NAV_FENCE_RETURN_POINT))
4133
4134
# Wait for guided return to vehicle calculated fence return location
4135
self.wait_distance_to_location(home_loc, 90, 110)
4136
self.wait_circling_point_with_radius(home_loc, 92)
4137
4138
self.progress("Test complete, disable fence and come home")
4139
self.do_fence_disable()
4140
self.fly_home_land_and_disarm()
4141
4142
def FenceDisableUnderAction(self):
4143
'''Tests Disabling fence while undergoing action caused by breach'''
4144
""" Fence breach will cause the vehicle to enter guided mode.
4145
Upon breach clear, check the vehicle is in the expected mode"""
4146
self.set_parameters({
4147
"FENCE_ALT_MIN": 50, # Sets the fence floor
4148
"FENCE_TYPE": 8, # Only use fence floor for breaches
4149
})
4150
self.wait_ready_to_arm()
4151
4152
def attempt_fence_breached_disable(start_mode, end_mode, expected_mode, action):
4153
self.set_parameter("FENCE_ACTION", action) # Set Fence Action to Guided
4154
self.change_mode(start_mode)
4155
self.arm_vehicle()
4156
self.do_fence_enable()
4157
self.assert_fence_enabled()
4158
self.wait_mode(expected_mode)
4159
self.do_fence_disable()
4160
self.assert_fence_disabled()
4161
self.wait_mode(end_mode)
4162
self.disarm_vehicle(force=True)
4163
4164
attempt_fence_breached_disable(start_mode="FBWA", end_mode="RTL", expected_mode="RTL", action=1)
4165
attempt_fence_breached_disable(start_mode="FBWA", end_mode="FBWA", expected_mode="GUIDED", action=6)
4166
attempt_fence_breached_disable(start_mode="FBWA", end_mode="FBWA", expected_mode="GUIDED", action=7)
4167
4168
def _MAV_CMD_DO_AUX_FUNCTION(self, run_cmd):
4169
'''Test triggering Auxiliary Functions via mavlink'''
4170
self.context_collect('STATUSTEXT')
4171
self.run_auxfunc(64, 2, run_cmd=run_cmd) # 64 == reverse throttle
4172
self.wait_statustext("RevThrottle: ENABLE", check_context=True)
4173
self.run_auxfunc(64, 0, run_cmd=run_cmd)
4174
self.wait_statustext("RevThrottle: DISABLE", check_context=True)
4175
self.run_auxfunc(65, 2, run_cmd=run_cmd) # 65 == GPS_DISABLE
4176
4177
self.start_subtest("Bad auxfunc")
4178
self.run_auxfunc(
4179
65231,
4180
2,
4181
want_result=mavutil.mavlink.MAV_RESULT_FAILED,
4182
run_cmd=run_cmd,
4183
)
4184
4185
self.start_subtest("Bad switchpos")
4186
self.run_auxfunc(
4187
62,
4188
17,
4189
want_result=mavutil.mavlink.MAV_RESULT_DENIED,
4190
run_cmd=run_cmd,
4191
)
4192
4193
def MAV_CMD_DO_AUX_FUNCTION(self):
4194
'''Test triggering Auxiliary Functions via mavlink'''
4195
self._MAV_CMD_DO_AUX_FUNCTION(run_cmd=self.run_cmd)
4196
self._MAV_CMD_DO_AUX_FUNCTION(run_cmd=self.run_cmd_int)
4197
4198
def FlyEachFrame(self):
4199
'''Fly each supported internal frame'''
4200
vinfo = vehicleinfo.VehicleInfo()
4201
vinfo_options = vinfo.options[self.vehicleinfo_key()]
4202
known_broken_frames = {
4203
"plane-tailsitter": "unstable in hover; unflyable in cruise",
4204
"quadplane-can" : "needs CAN periph",
4205
"stratoblimp" : "not expected to fly normally",
4206
"glider" : "needs balloon lift",
4207
}
4208
for frame in sorted(vinfo_options["frames"].keys()):
4209
self.start_subtest("Testing frame (%s)" % str(frame))
4210
if frame in known_broken_frames:
4211
self.progress("Actually, no I'm not - it is known-broken (%s)" %
4212
(known_broken_frames[frame]))
4213
continue
4214
frame_bits = vinfo_options["frames"][frame]
4215
print("frame_bits: %s" % str(frame_bits))
4216
if frame_bits.get("external", False):
4217
self.progress("Actually, no I'm not - it is an external simulation")
4218
continue
4219
model = frame_bits.get("model", frame)
4220
defaults = self.model_defaults_filepath(frame)
4221
if not isinstance(defaults, list):
4222
defaults = [defaults]
4223
self.customise_SITL_commandline(
4224
[],
4225
defaults_filepath=defaults,
4226
model=model,
4227
wipe=True,
4228
)
4229
mission_file = "basic.txt"
4230
quadplane = self.get_parameter('Q_ENABLE')
4231
if quadplane:
4232
mission_file = "basic-quadplane.txt"
4233
tailsitter = self.get_parameter('Q_TAILSIT_ENABLE')
4234
if tailsitter:
4235
# tailsitter needs extra re-boot to pick up the rotated AHRS view
4236
self.reboot_sitl()
4237
self.wait_ready_to_arm()
4238
self.arm_vehicle()
4239
self.fly_mission(mission_file, strict=False, quadplane=quadplane, mission_timeout=400.0)
4240
self.wait_disarmed()
4241
4242
def AutoLandMode(self):
4243
'''Test AUTOLAND mode'''
4244
self.customise_SITL_commandline(["--home", "-35.362938,149.165085,585,173"])
4245
self.context_collect('STATUSTEXT')
4246
self.takeoff(alt=80, mode='TAKEOFF')
4247
self.wait_text("Autoland direction", check_context=True)
4248
self.change_mode(26)
4249
self.wait_disarmed(120)
4250
self.progress("Check the landed heading matches takeoff")
4251
self.wait_heading(173, accuracy=5, timeout=1)
4252
loc = mavutil.location(-35.362938, 149.165085, 585, 173)
4253
if self.get_distance(loc, self.mav.location()) > 35:
4254
raise NotAchievedException("Did not land close to home")
4255
4256
def RCDisableAirspeedUse(self):
4257
'''Test RC DisableAirspeedUse option'''
4258
self.set_parameter("RC9_OPTION", 106)
4259
self.delay_sim_time(5)
4260
self.set_rc(9, 1000)
4261
self.wait_sensor_state(
4262
mavutil.mavlink.MAV_SYS_STATUS_SENSOR_DIFFERENTIAL_PRESSURE,
4263
True,
4264
True,
4265
True)
4266
self.set_rc(9, 2000)
4267
self.wait_sensor_state(
4268
mavutil.mavlink.MAV_SYS_STATUS_SENSOR_DIFFERENTIAL_PRESSURE,
4269
True,
4270
False,
4271
True)
4272
self.set_rc(9, 1000)
4273
self.wait_sensor_state(
4274
mavutil.mavlink.MAV_SYS_STATUS_SENSOR_DIFFERENTIAL_PRESSURE,
4275
True,
4276
True,
4277
True)
4278
4279
def WatchdogHome(self):
4280
'''Ensure home is restored after watchdog reset'''
4281
if self.gdb:
4282
# we end up signalling the wrong process. I think.
4283
# Probably need to have a "sitl_pid()" method to get the
4284
# ardupilot process's PID.
4285
self.progress("######## Skipping WatchdogHome test under GDB")
4286
return
4287
4288
ex = None
4289
try:
4290
self.progress("Enabling watchdog")
4291
self.set_parameter("BRD_OPTIONS", 1 << 0)
4292
self.reboot_sitl()
4293
self.wait_ready_to_arm()
4294
self.progress("Explicitly setting home to a known location")
4295
orig_home = self.poll_home_position()
4296
new_home = orig_home
4297
new_home.latitude = new_home.latitude + 1000
4298
new_home.longitude = new_home.longitude + 2000
4299
new_home.altitude = new_home.altitude + 300000 # 300 metres
4300
self.run_cmd_int(
4301
mavutil.mavlink.MAV_CMD_DO_SET_HOME,
4302
p5=new_home.latitude,
4303
p6=new_home.longitude,
4304
p7=new_home.altitude/1000.0, # mm => m
4305
)
4306
old_bootcount = self.get_parameter('STAT_BOOTCNT')
4307
self.progress("Forcing watchdog reset")
4308
os.kill(self.sitl.pid, signal.SIGALRM)
4309
self.detect_and_handle_reboot(old_bootcount)
4310
self.wait_statustext("WDG:")
4311
self.wait_statustext("IMU1 is using GPS") # won't be come armable
4312
self.progress("Verifying home position")
4313
post_reboot_home = self.poll_home_position()
4314
delta = self.get_distance_int(new_home, post_reboot_home)
4315
max_delta = 1
4316
if delta > max_delta:
4317
raise NotAchievedException(
4318
"New home not where it should be (dist=%f) (want=%s) (got=%s)" %
4319
(delta, str(new_home), str(post_reboot_home)))
4320
except Exception as e:
4321
self.print_exception_caught(e)
4322
ex = e
4323
4324
self.reboot_sitl()
4325
4326
if ex is not None:
4327
raise ex
4328
4329
def AUTOTUNE(self):
4330
'''Test AutoTune mode'''
4331
self.takeoff(100)
4332
self.change_mode('AUTOTUNE')
4333
self.context_collect('STATUSTEXT')
4334
tstart = self.get_sim_time()
4335
axis = "Roll"
4336
rc_value = 1000
4337
while True:
4338
timeout = 600
4339
if self.get_sim_time() - tstart > timeout:
4340
raise NotAchievedException("Did not complete within %u seconds" % timeout)
4341
try:
4342
m = self.wait_statustext("%s: Finished" % axis, check_context=True, timeout=0.1)
4343
self.progress("Got %s" % str(m))
4344
if axis == "Roll":
4345
axis = "Pitch"
4346
elif axis == "Pitch":
4347
break
4348
else:
4349
raise ValueError("Bug: %s" % axis)
4350
except AutoTestTimeoutException:
4351
pass
4352
self.delay_sim_time(1)
4353
4354
if rc_value == 1000:
4355
rc_value = 2000
4356
elif rc_value == 2000:
4357
rc_value = 1000
4358
elif rc_value == 1000:
4359
rc_value = 2000
4360
else:
4361
raise ValueError("Bug")
4362
4363
if axis == "Roll":
4364
self.set_rc(1, rc_value)
4365
self.set_rc(2, 1500)
4366
elif axis == "Pitch":
4367
self.set_rc(1, 1500)
4368
self.set_rc(2, rc_value)
4369
else:
4370
raise ValueError("Bug")
4371
4372
tdelta = self.get_sim_time() - tstart
4373
self.progress("Finished in %0.1f seconds" % (tdelta,))
4374
4375
self.set_rc(1, 1500)
4376
self.set_rc(2, 1500)
4377
4378
self.change_mode('FBWA')
4379
self.fly_home_land_and_disarm(timeout=tdelta+240)
4380
4381
def AutotuneFiltering(self):
4382
'''Test AutoTune mode with filter updates disabled'''
4383
self.set_parameters({
4384
"AUTOTUNE_OPTIONS": 3,
4385
# some filtering is required for autotune to complete
4386
"RLL_RATE_FLTD": 10,
4387
"PTCH_RATE_FLTD": 10,
4388
"RLL_RATE_FLTT": 20,
4389
"PTCH_RATE_FLTT": 20,
4390
})
4391
self.AUTOTUNE()
4392
4393
def LandingDrift(self):
4394
'''Circuit with baro drift'''
4395
self.customise_SITL_commandline([], wipe=True)
4396
4397
self.set_analog_rangefinder_parameters()
4398
4399
self.set_parameters({
4400
"SIM_BARO_DRIFT": -0.02,
4401
"SIM_TERRAIN": 0,
4402
"RNGFND_LANDING": 1,
4403
"LAND_SLOPE_RCALC": 2,
4404
"LAND_ABORT_DEG": 1,
4405
})
4406
4407
self.reboot_sitl()
4408
4409
self.wait_ready_to_arm()
4410
self.arm_vehicle()
4411
4412
# Load and start mission
4413
self.load_mission("ap-circuit.txt", strict=True)
4414
self.set_current_waypoint(1, check_afterwards=True)
4415
self.change_mode('AUTO')
4416
self.wait_current_waypoint(1, timeout=5)
4417
self.wait_groundspeed(0, 10, timeout=5)
4418
4419
# Wait for landing waypoint
4420
self.wait_current_waypoint(9, timeout=1200)
4421
4422
# Wait for landing restart
4423
self.wait_current_waypoint(5, timeout=60)
4424
4425
# Wait for landing waypoint (second attempt)
4426
self.wait_current_waypoint(9, timeout=1200)
4427
4428
self.wait_disarmed(timeout=180)
4429
4430
def TakeoffAuto1(self):
4431
'''Test the behaviour of an AUTO takeoff, pt1.'''
4432
'''
4433
Conditions:
4434
- ARSPD_USE=1
4435
- TKOFF_OPTIONS[0]=0
4436
- TKOFF_THR_MAX < THR_MAX
4437
'''
4438
4439
self.customise_SITL_commandline(
4440
[],
4441
model='plane-catapult',
4442
defaults_filepath=self.model_defaults_filepath("plane")
4443
)
4444
self.set_parameters({
4445
"ARSPD_USE": 1.0,
4446
"THR_MAX": 100.0,
4447
"TKOFF_THR_MAX": 80.0,
4448
"TKOFF_THR_MINACC": 3.0,
4449
"TECS_PITCH_MAX": 35.0,
4450
"PTCH_LIM_MAX_DEG": 35.0,
4451
"RTL_AUTOLAND": 2, # The mission contains a DO_LAND_START item.
4452
})
4453
4454
# Load and start mission. It contains a MAV_CMD_NAV_TAKEOFF item at 100m.
4455
self.wait_ready_to_arm()
4456
4457
# Load and start mission. It contains a MAV_CMD_NAV_TAKEOFF item at 100m.
4458
self.load_mission("catapult.txt", strict=True)
4459
self.change_mode('AUTO')
4460
4461
self.arm_vehicle()
4462
4463
# Throw the catapult.
4464
self.set_servo(7, 2000)
4465
4466
# Wait until we're midway through the climb.
4467
test_alt = 50
4468
self.wait_altitude(test_alt, test_alt+2, relative=True)
4469
4470
# Ensure that by then the aircraft still goes at max allowed throttle.
4471
target_throttle = 1000+10*(self.get_parameter("TKOFF_THR_MAX"))
4472
self.assert_servo_channel_range(3, target_throttle-10, target_throttle+10)
4473
4474
# Wait for landing waypoint.
4475
self.wait_current_waypoint(11, timeout=1200)
4476
self.wait_disarmed(120)
4477
4478
def TakeoffAuto2(self):
4479
'''Test the behaviour of an AUTO takeoff, pt2.'''
4480
'''
4481
Conditions:
4482
- ARSPD_USE=0
4483
- TKOFF_OPTIONS[0]=0
4484
- TKOFF_THR_MAX > THR_MAX
4485
'''
4486
4487
self.customise_SITL_commandline(
4488
[],
4489
model='plane-catapult',
4490
defaults_filepath=self.model_defaults_filepath("plane")
4491
)
4492
self.set_parameters({
4493
"ARSPD_USE": 0.0,
4494
"THR_MAX": 80.0,
4495
"TKOFF_THR_MAX": 100.0,
4496
"TKOFF_THR_MINACC": 3.0,
4497
"TECS_PITCH_MAX": 35.0,
4498
"PTCH_LIM_MAX_DEG": 35.0,
4499
"RTL_AUTOLAND": 2, # The mission contains a DO_LAND_START item.
4500
})
4501
4502
# Load and start mission. It contains a MAV_CMD_NAV_TAKEOFF item at 100m.
4503
self.wait_ready_to_arm()
4504
4505
# Load and start mission. It contains a MAV_CMD_NAV_TAKEOFF item at 100m.
4506
self.load_mission("catapult.txt", strict=True)
4507
self.change_mode('AUTO')
4508
4509
self.arm_vehicle()
4510
4511
# Throw the catapult.
4512
self.set_servo(7, 2000)
4513
4514
# Wait until we're midway through the climb.
4515
test_alt = 50
4516
self.wait_altitude(test_alt, test_alt+2, relative=True)
4517
4518
# Ensure that by then the aircraft still goes at max allowed throttle.
4519
target_throttle = 1000+10*(self.get_parameter("TKOFF_THR_MAX"))
4520
self.assert_servo_channel_range(3, target_throttle-10, target_throttle+10)
4521
4522
# Wait for landing waypoint.
4523
self.wait_current_waypoint(11, timeout=1200)
4524
self.wait_disarmed(120)
4525
4526
def TakeoffAuto3(self):
4527
'''Test the behaviour of an AUTO takeoff, pt3.'''
4528
'''
4529
Conditions:
4530
- ARSPD_USE=1
4531
- TKOFF_OPTIONS[0]=1
4532
'''
4533
4534
self.customise_SITL_commandline(
4535
[],
4536
model='plane-catapult',
4537
defaults_filepath=self.model_defaults_filepath("plane")
4538
)
4539
self.set_parameters({
4540
"ARSPD_USE": 1.0,
4541
"THR_MAX": 80.0,
4542
"THR_MIN": 0.0,
4543
"TKOFF_OPTIONS": 1.0,
4544
"TKOFF_THR_MAX": 100.0,
4545
"TKOFF_THR_MINACC": 3.0,
4546
"TECS_PITCH_MAX": 35.0,
4547
"TKOFF_THR_MAX_T": 3.0,
4548
"PTCH_LIM_MAX_DEG": 35.0,
4549
"RTL_AUTOLAND": 2, # The mission contains a DO_LAND_START item.
4550
})
4551
4552
# Load and start mission. It contains a MAV_CMD_NAV_TAKEOFF item at 100m.
4553
self.wait_ready_to_arm()
4554
4555
# Load and start mission. It contains a MAV_CMD_NAV_TAKEOFF item at 100m.
4556
self.load_mission("catapult.txt", strict=True)
4557
self.change_mode('AUTO')
4558
4559
self.arm_vehicle()
4560
4561
# Throw the catapult.
4562
self.set_servo(7, 2000)
4563
4564
# Ensure that TKOFF_THR_MAX_T is respected.
4565
self.delay_sim_time(self.get_parameter("TKOFF_THR_MAX_T")-1)
4566
target_throttle = 1000+10*(self.get_parameter("TKOFF_THR_MAX"))
4567
self.assert_servo_channel_range(3, target_throttle-10, target_throttle+10)
4568
4569
# Ensure that after that the aircraft does not go full throttle anymore.
4570
test_alt = 50
4571
self.wait_altitude(test_alt, test_alt+2, relative=True)
4572
w = vehicle_test_suite.WaitAndMaintainServoChannelValue(
4573
self,
4574
3, # throttle
4575
1000+10*self.get_parameter("TKOFF_THR_MAX")-10,
4576
comparator=operator.lt,
4577
minimum_duration=1,
4578
)
4579
w.run()
4580
4581
# Wait for landing waypoint.
4582
self.wait_current_waypoint(11, timeout=1200)
4583
self.wait_disarmed(120)
4584
4585
def TakeoffAuto4(self):
4586
'''Test the behaviour of an AUTO takeoff, pt4.'''
4587
'''
4588
Conditions:
4589
- ARSPD_USE=0
4590
- TKOFF_OPTIONS[0]=1
4591
'''
4592
4593
self.customise_SITL_commandline(
4594
[],
4595
model='plane-catapult',
4596
defaults_filepath=self.model_defaults_filepath("plane")
4597
)
4598
self.set_parameters({
4599
"ARSPD_USE": 0.0,
4600
"THR_MAX": 80.0,
4601
"THR_MIN": 0.0,
4602
"TKOFF_OPTIONS": 1.0,
4603
"TKOFF_THR_MAX": 100.0,
4604
"TKOFF_THR_MINACC": 3.0,
4605
"TECS_PITCH_MAX": 35.0,
4606
"TKOFF_THR_MAX_T": 3.0,
4607
"PTCH_LIM_MAX_DEG": 35.0,
4608
"RTL_AUTOLAND": 2, # The mission contains a DO_LAND_START item.
4609
})
4610
4611
self.wait_ready_to_arm()
4612
4613
# Load and start mission. It contains a MAV_CMD_NAV_TAKEOFF item at 100m.
4614
self.load_mission("catapult.txt", strict=True)
4615
self.change_mode('AUTO')
4616
4617
self.arm_vehicle()
4618
4619
# Throw the catapult.
4620
self.set_servo(7, 2000)
4621
4622
# Ensure that TKOFF_THR_MAX_T is respected.
4623
self.delay_sim_time(self.get_parameter("TKOFF_THR_MAX_T")-1)
4624
target_throttle = 1000+10*(self.get_parameter("TKOFF_THR_MAX"))
4625
self.assert_servo_channel_range(3, target_throttle-10, target_throttle+10)
4626
4627
# Ensure that after that the aircraft still goes to maximum throttle.
4628
test_alt = 50
4629
self.wait_altitude(test_alt, test_alt+2, relative=True)
4630
target_throttle = 1000+10*(self.get_parameter("TKOFF_THR_MAX"))
4631
self.assert_servo_channel_range(3, target_throttle-10, target_throttle+10)
4632
4633
# Wait for landing waypoint.
4634
self.wait_current_waypoint(11, timeout=1200)
4635
self.wait_disarmed(120)
4636
4637
def TakeoffTakeoff1(self):
4638
'''Test the behaviour of a takeoff in TAKEOFF mode, pt1.'''
4639
'''
4640
Conditions:
4641
- ARSPD_USE=1
4642
- TKOFF_OPTIONS[0]=0
4643
- TKOFF_THR_MAX < THR_MAX
4644
'''
4645
4646
self.customise_SITL_commandline(
4647
[],
4648
model='plane-catapult',
4649
defaults_filepath=self.model_defaults_filepath("plane")
4650
)
4651
self.set_parameters({
4652
"ARSPD_USE": 1.0,
4653
"THR_MAX": 100.0,
4654
"TKOFF_LVL_ALT": 30.0,
4655
"TKOFF_ALT": 80.0,
4656
"TKOFF_OPTIONS": 0.0,
4657
"TKOFF_THR_MINACC": 3.0,
4658
"TKOFF_THR_MAX": 80.0,
4659
"TECS_PITCH_MAX": 35.0,
4660
"PTCH_LIM_MAX_DEG": 35.0,
4661
})
4662
self.change_mode("TAKEOFF")
4663
4664
self.wait_ready_to_arm()
4665
self.arm_vehicle()
4666
4667
# Throw the catapult.
4668
self.set_servo(7, 2000)
4669
4670
# Check whether we're at max throttle below TKOFF_LVL_ALT.
4671
test_alt = self.get_parameter("TKOFF_LVL_ALT")-10
4672
self.wait_altitude(test_alt, test_alt+2, relative=True)
4673
target_throttle = 1000+10*(self.get_parameter("TKOFF_THR_MAX"))
4674
self.assert_servo_channel_range(3, target_throttle-10, target_throttle+10)
4675
4676
# Check whether we're still at max throttle past TKOFF_LVL_ALT.
4677
test_alt = self.get_parameter("TKOFF_LVL_ALT")+10
4678
self.wait_altitude(test_alt, test_alt+2, relative=True)
4679
target_throttle = 1000+10*(self.get_parameter("TKOFF_THR_MAX"))
4680
self.assert_servo_channel_range(3, target_throttle-10, target_throttle+10)
4681
4682
# Wait for the takeoff to complete.
4683
target_alt = self.get_parameter("TKOFF_ALT")
4684
self.wait_altitude(target_alt-5, target_alt, relative=True)
4685
4686
# Wait a bit for the Takeoff altitude to settle.
4687
self.delay_sim_time(5)
4688
4689
self.fly_home_land_and_disarm()
4690
4691
def TakeoffTakeoff2(self):
4692
'''Test the behaviour of a takeoff in TAKEOFF mode, pt2.'''
4693
'''
4694
Conditions:
4695
- ARSPD_USE=1
4696
- TKOFF_OPTIONS[0]=1
4697
- TKOFF_THR_MAX < THR_MAX
4698
'''
4699
4700
self.customise_SITL_commandline(
4701
[],
4702
model='plane-catapult',
4703
defaults_filepath=self.model_defaults_filepath("plane")
4704
)
4705
self.set_parameters({
4706
"ARSPD_USE": 1.0,
4707
"THR_MAX": 100.0,
4708
"TKOFF_LVL_ALT": 30.0,
4709
"TKOFF_ALT": 80.0,
4710
"TKOFF_OPTIONS": 1.0,
4711
"TKOFF_THR_MINACC": 3.0,
4712
"TKOFF_THR_MAX": 80.0,
4713
"TECS_PITCH_MAX": 35.0,
4714
"PTCH_LIM_MAX_DEG": 35.0,
4715
})
4716
self.change_mode("TAKEOFF")
4717
4718
self.wait_ready_to_arm()
4719
self.arm_vehicle()
4720
4721
# Throw the catapult.
4722
self.set_servo(7, 2000)
4723
4724
# Check whether we're at max throttle below TKOFF_LVL_ALT.
4725
test_alt = self.get_parameter("TKOFF_LVL_ALT")-10
4726
self.wait_altitude(test_alt, test_alt+2, relative=True)
4727
target_throttle = 1000+10*(self.get_parameter("TKOFF_THR_MAX"))
4728
self.assert_servo_channel_range(3, target_throttle-10, target_throttle+10)
4729
4730
# Check whether we've receded from max throttle past TKOFF_LVL_ALT.
4731
test_alt = self.get_parameter("TKOFF_LVL_ALT")+10
4732
self.wait_altitude(test_alt, test_alt+2, relative=True)
4733
thr_min = 1000+10*(self.get_parameter("TKOFF_THR_MIN"))-1
4734
thr_max = 1000+10*(self.get_parameter("TKOFF_THR_MAX"))-10
4735
self.assert_servo_channel_range(3, thr_min, thr_max)
4736
4737
# Wait for the takeoff to complete.
4738
target_alt = self.get_parameter("TKOFF_ALT")
4739
self.wait_altitude(target_alt-5, target_alt, relative=True)
4740
4741
# Wait a bit for the Takeoff altitude to settle.
4742
self.delay_sim_time(5)
4743
4744
self.fly_home_land_and_disarm()
4745
4746
def TakeoffTakeoff3(self):
4747
'''Test the behaviour of a takeoff in TAKEOFF mode, pt3.'''
4748
'''
4749
This is the same as case #1, but with disabled airspeed sensor.
4750
4751
Conditions:
4752
- ARSPD_USE=0
4753
- TKOFF_OPTIONS[0]=0
4754
- TKOFF_THR_MAX < THR_MAX
4755
'''
4756
4757
self.customise_SITL_commandline(
4758
[],
4759
model='plane-catapult',
4760
defaults_filepath=self.model_defaults_filepath("plane")
4761
)
4762
self.set_parameters({
4763
"ARSPD_USE": 0.0,
4764
"THR_MAX": 100.0,
4765
"TKOFF_DIST": 400.0,
4766
"TKOFF_LVL_ALT": 30.0,
4767
"TKOFF_ALT": 100.0,
4768
"TKOFF_OPTIONS": 0.0,
4769
"TKOFF_THR_MINACC": 3.0,
4770
"TKOFF_THR_MAX": 80.0,
4771
"TECS_PITCH_MAX": 35.0,
4772
"PTCH_LIM_MAX_DEG": 35.0,
4773
})
4774
self.change_mode("TAKEOFF")
4775
4776
self.wait_ready_to_arm()
4777
self.arm_vehicle()
4778
4779
# Throw the catapult.
4780
self.set_servo(7, 2000)
4781
4782
# we expect to maintain this throttle level past the takeoff
4783
# altitude through to our takeoff altitude:
4784
expected_takeoff_throttle = 1000+10*self.get_parameter("TKOFF_THR_MAX")
4785
4786
# Check whether we're at max throttle below TKOFF_LVL_ALT.
4787
test_alt = self.get_parameter("TKOFF_LVL_ALT")-10
4788
self.wait_altitude(test_alt, test_alt+2, relative=True)
4789
w = vehicle_test_suite.WaitAndMaintainServoChannelValue(
4790
self,
4791
3, # throttle
4792
expected_takeoff_throttle,
4793
epsilon=10,
4794
minimum_duration=1,
4795
)
4796
w.run()
4797
4798
# Check whether we're still at max throttle past TKOFF_LVL_ALT.
4799
test_alt = self.get_parameter("TKOFF_LVL_ALT")+10
4800
self.wait_altitude(test_alt, test_alt+2, relative=True)
4801
4802
w = vehicle_test_suite.WaitAndMaintainServoChannelValue(
4803
self,
4804
3, # throttle
4805
expected_takeoff_throttle,
4806
epsilon=10,
4807
minimum_duration=1,
4808
)
4809
w.run()
4810
4811
# Wait for the takeoff to complete.
4812
target_alt = self.get_parameter("TKOFF_ALT")
4813
self.wait_altitude(target_alt-2.5, target_alt+2.5, relative=True, minimum_duration=10, timeout=30)
4814
4815
self.reboot_sitl(force=True)
4816
4817
def TakeoffTakeoff4(self):
4818
'''Test the behaviour of a takeoff in TAKEOFF mode, pt4.'''
4819
'''
4820
This is the same as case #3, but with almost stock parameters and without a catapult.
4821
4822
Conditions:
4823
- ARSPD_USE=0
4824
'''
4825
4826
self.customise_SITL_commandline(
4827
[],
4828
model='plane-catapult',
4829
defaults_filepath=self.model_defaults_filepath("plane")
4830
)
4831
self.set_parameters({
4832
"ARSPD_USE": 0.0,
4833
})
4834
self.change_mode("TAKEOFF")
4835
4836
self.wait_ready_to_arm()
4837
self.arm_vehicle()
4838
4839
# Check whether we're at max throttle below TKOFF_LVL_ALT.
4840
test_alt = self.get_parameter("TKOFF_LVL_ALT")-10
4841
self.wait_altitude(test_alt, test_alt+2, relative=True)
4842
target_throttle = 1000+10*(self.get_parameter("THR_MAX"))
4843
self.assert_servo_channel_range(3, target_throttle-10, target_throttle+10)
4844
4845
# Check whether we're still at max throttle past TKOFF_LVL_ALT.
4846
test_alt = self.get_parameter("TKOFF_LVL_ALT")+10
4847
self.wait_altitude(test_alt, test_alt+2, relative=True)
4848
target_throttle = 1000+10*(self.get_parameter("THR_MAX"))
4849
self.assert_servo_channel_range(3, target_throttle-10, target_throttle+10)
4850
4851
# Wait for the takeoff to complete.
4852
target_alt = self.get_parameter("TKOFF_ALT")
4853
self.wait_altitude(target_alt-5, target_alt, relative=True)
4854
4855
# Wait a bit for the Takeoff altitude to settle.
4856
self.delay_sim_time(5)
4857
4858
self.fly_home_land_and_disarm()
4859
4860
def TakeoffTakeoff5(self):
4861
'''Test the behaviour of a takeoff with no compass'''
4862
self.set_parameters({
4863
"COMPASS_USE": 0,
4864
"COMPASS_USE2": 0,
4865
"COMPASS_USE3": 0,
4866
})
4867
import copy
4868
start_loc = copy.copy(SITL_START_LOCATION)
4869
start_loc.heading = 175
4870
self.customise_SITL_commandline(["--home=%.9f,%.9f,%.2f,%.1f" % (
4871
start_loc.lat, start_loc.lng, start_loc.alt, start_loc.heading)])
4872
self.reboot_sitl()
4873
self.change_mode("TAKEOFF")
4874
4875
# waiting for the EKF to be happy won't work
4876
self.delay_sim_time(20)
4877
self.arm_vehicle()
4878
4879
target_alt = self.get_parameter("TKOFF_ALT")
4880
self.wait_altitude(target_alt-5, target_alt, relative=True)
4881
4882
# Wait a bit for the Takeoff altitude to settle.
4883
self.delay_sim_time(5)
4884
4885
bearing_margin = 35
4886
loc = self.mav.location()
4887
bearing_from_home = self.get_bearing(start_loc, loc)
4888
if bearing_from_home < 0:
4889
bearing_from_home += 360
4890
if abs(bearing_from_home - start_loc.heading) > bearing_margin:
4891
raise NotAchievedException(f"Did not takeoff in the right direction {bearing_from_home}")
4892
4893
self.fly_home_land_and_disarm()
4894
4895
def TakeoffGround(self):
4896
'''Test a rolling TAKEOFF.'''
4897
4898
self.set_parameters({
4899
"TKOFF_ROTATE_SPD": 15.0,
4900
})
4901
self.change_mode("TAKEOFF")
4902
4903
self.wait_ready_to_arm()
4904
self.arm_vehicle()
4905
4906
# Check that we demand minimum pitch below rotation speed.
4907
self.wait_groundspeed(8, 10)
4908
m = self.assert_receive_message('NAV_CONTROLLER_OUTPUT', timeout=5)
4909
nav_pitch = m.nav_pitch
4910
if nav_pitch > 5.1 or nav_pitch < 4.9:
4911
raise NotAchievedException(f"Did not achieve correct takeoff pitch ({nav_pitch}).")
4912
4913
# Check whether we've achieved correct target pitch after rotation.
4914
self.wait_groundspeed(23, 24)
4915
m = self.assert_receive_message('NAV_CONTROLLER_OUTPUT', timeout=5)
4916
nav_pitch = m.nav_pitch
4917
if nav_pitch > 15.1 or nav_pitch < 14.9:
4918
raise NotAchievedException(f"Did not achieve correct takeoff pitch ({nav_pitch}).")
4919
4920
self.fly_home_land_and_disarm()
4921
4922
def TakeoffIdleThrottle(self):
4923
'''Apply idle throttle before takeoff.'''
4924
self.customise_SITL_commandline(
4925
[],
4926
model='plane-catapult',
4927
defaults_filepath=self.model_defaults_filepath("plane")
4928
)
4929
self.set_parameters({
4930
"TKOFF_THR_IDLE": 20.0,
4931
"TKOFF_THR_MINSPD": 3.0,
4932
})
4933
self.change_mode("TAKEOFF")
4934
4935
self.wait_ready_to_arm()
4936
self.arm_vehicle()
4937
4938
# Ensure that the throttle rises to idle throttle.
4939
expected_idle_throttle = 1000+10*self.get_parameter("TKOFF_THR_IDLE")
4940
self.assert_servo_channel_range(3, expected_idle_throttle-10, expected_idle_throttle+10)
4941
4942
# Launch the catapult
4943
self.set_servo(6, 1000)
4944
4945
self.delay_sim_time(5)
4946
self.change_mode('RTL')
4947
4948
self.fly_home_land_and_disarm()
4949
4950
def TakeoffBadLevelOff(self):
4951
'''Ensure that the takeoff can be completed under 0 pitch demand.'''
4952
'''
4953
When using no airspeed, the pitch level-off will eventually command 0
4954
pitch demand. Ensure that the plane can climb the final 2m to deem the
4955
takeoff complete.
4956
'''
4957
4958
self.customise_SITL_commandline(
4959
[],
4960
model='plane-catapult',
4961
defaults_filepath=self.model_defaults_filepath("plane")
4962
)
4963
self.set_parameters({
4964
"ARSPD_USE": 0.0,
4965
"PTCH_TRIM_DEG": -10.0,
4966
"RTL_AUTOLAND": 2, # The mission contains a DO_LAND_START item.
4967
"TKOFF_ALT": 50.0,
4968
"TKOFF_DIST": 1000.0,
4969
"TKOFF_THR_MAX": 75.0,
4970
"TKOFF_THR_MINACC": 3.0,
4971
})
4972
4973
self.load_mission("flaps_tkoff_50.txt")
4974
self.change_mode('AUTO')
4975
4976
self.wait_ready_to_arm()
4977
self.arm_vehicle()
4978
4979
# Throw the catapult.
4980
self.set_servo(7, 2000)
4981
4982
# Wait until we've reached the takeoff altitude.
4983
target_alt = 50
4984
self.wait_altitude(target_alt-1, target_alt+1, relative=True, timeout=30)
4985
4986
self.delay_sim_time(5)
4987
4988
self.disarm_vehicle(force=True)
4989
4990
def DCMFallback(self):
4991
'''Really annoy the EKF and force fallback'''
4992
self.reboot_sitl()
4993
self.delay_sim_time(30)
4994
4995
self.takeoff(50)
4996
self.change_mode('CIRCLE')
4997
self.context_push()
4998
self.context_collect('STATUSTEXT')
4999
self.set_parameters({
5000
"EK3_POS_I_GATE": 0,
5001
"SIM_GPS1_HZ": 1,
5002
"SIM_GPS1_LAG_MS": 1000,
5003
})
5004
self.wait_statustext("DCM Active", check_context=True, timeout=60)
5005
self.wait_statustext("EKF3 Active", check_context=True)
5006
self.wait_statustext("DCM Active", check_context=True)
5007
self.wait_statustext("EKF3 Active", check_context=True)
5008
self.wait_statustext("DCM Active", check_context=True)
5009
self.wait_statustext("EKF3 Active", check_context=True)
5010
self.context_stop_collecting('STATUSTEXT')
5011
5012
self.fly_home_land_and_disarm()
5013
self.context_pop()
5014
self.reboot_sitl()
5015
5016
def ForcedDCM(self):
5017
'''Switch to DCM mid-flight'''
5018
self.wait_ready_to_arm()
5019
self.arm_vehicle()
5020
5021
self.takeoff(50)
5022
self.context_collect('STATUSTEXT')
5023
self.set_parameter("AHRS_EKF_TYPE", 0)
5024
self.wait_statustext("DCM Active", check_context=True)
5025
self.context_stop_collecting('STATUSTEXT')
5026
5027
self.fly_home_land_and_disarm()
5028
5029
def EFITest(self, efi_type, name, sim_name, check_fuel_flow=True):
5030
'''method to be called by EFI tests'''
5031
self.start_subtest("EFI Test for (%s)" % name)
5032
self.assert_not_receiving_message('EFI_STATUS')
5033
self.set_parameters({
5034
'SIM_EFI_TYPE': efi_type,
5035
'EFI_TYPE': efi_type,
5036
'SERIAL5_PROTOCOL': 24,
5037
'RPM1_TYPE': 10,
5038
})
5039
5040
self.customise_SITL_commandline(
5041
["--serial5=sim:%s" % sim_name,
5042
],
5043
)
5044
self.wait_ready_to_arm()
5045
5046
baro_m = self.assert_receive_message("SCALED_PRESSURE")
5047
self.progress(self.dump_message_verbose(baro_m))
5048
baro_temperature = baro_m.temperature / 100.0 # cDeg->deg
5049
m = self.assert_received_message_field_values("EFI_STATUS", {
5050
"throttle_out": 0,
5051
"health": 1,
5052
}, very_verbose=1)
5053
5054
if abs(baro_temperature - m.intake_manifold_temperature) > 1:
5055
raise NotAchievedException(
5056
"Bad intake manifold temperature (want=%f got=%f)" %
5057
(baro_temperature, m.intake_manifold_temperature))
5058
5059
self.arm_vehicle()
5060
5061
self.set_rc(3, 1300)
5062
5063
tstart = self.get_sim_time()
5064
while True:
5065
now = self.get_sim_time_cached()
5066
if now - tstart > 10:
5067
raise NotAchievedException("RPM1 and EFI_STATUS.rpm did not match")
5068
rpm_m = self.assert_receive_message("RPM", verbose=1)
5069
want_rpm = 1000
5070
if rpm_m.rpm1 < want_rpm:
5071
continue
5072
5073
m = self.assert_receive_message("EFI_STATUS", verbose=1)
5074
if abs(m.rpm - rpm_m.rpm1) > 100:
5075
continue
5076
5077
break
5078
5079
self.progress("now we're started, check a few more values")
5080
# note that megasquirt drver doesn't send throttle, so throttle_out is zero!
5081
m = self.assert_received_message_field_values("EFI_STATUS", {
5082
"health": 1,
5083
}, very_verbose=1)
5084
m = self.wait_message_field_values("EFI_STATUS", {
5085
"throttle_position": 31,
5086
"intake_manifold_temperature": 28,
5087
}, very_verbose=1, epsilon=2)
5088
5089
if check_fuel_flow:
5090
if abs(m.fuel_flow - 0.2) < 0.0001:
5091
raise NotAchievedException("Expected fuel flow")
5092
5093
self.set_rc(3, 1000)
5094
5095
# need to force disarm as the is_flying flag can trigger with the engine running
5096
self.disarm_vehicle(force=True)
5097
5098
def MegaSquirt(self):
5099
'''test MegaSquirt driver'''
5100
self.EFITest(
5101
1, "MegaSquirt", "megasquirt",
5102
check_fuel_flow=False,
5103
)
5104
5105
def Hirth(self):
5106
'''Test Hirth EFI'''
5107
self.EFITest(8, "Hirth", "hirth")
5108
5109
def GlideSlopeThresh(self):
5110
'''Test rebuild glide slope if above and climbing'''
5111
5112
# Test that GLIDE_SLOPE_THRESHOLD correctly controls re-planning glide slope
5113
# in the scenario that aircraft is above planned slope and slope is positive (climbing).
5114
#
5115
#
5116
# Behaviour with GLIDE_SLOPE_THRESH = 0 (no slope replanning)
5117
# (2).. __(4)
5118
# | \..__/
5119
# | __/
5120
# (3)
5121
#
5122
# Behaviour with GLIDE_SLOPE_THRESH = 5 (slope replanning when >5m error)
5123
# (2)........__(4)
5124
# | __/
5125
# | __/
5126
# (3)
5127
# Solid is plan, dots are actual flightpath.
5128
5129
self.load_mission('rapid-descent-then-climb.txt', strict=False)
5130
5131
self.set_current_waypoint(1)
5132
self.change_mode('AUTO')
5133
self.wait_ready_to_arm()
5134
self.arm_vehicle()
5135
5136
#
5137
# Initial run with GLIDE_SLOPE_THR = 5 (default).
5138
#
5139
self.set_parameter("GLIDE_SLOPE_THR", 5)
5140
5141
# Wait for waypoint commanding rapid descent, followed by climb.
5142
self.wait_current_waypoint(5, timeout=1200)
5143
5144
# Altitude should not descend significantly below the initial altitude
5145
init_altitude = self.get_altitude(relative=True, timeout=2)
5146
timeout = 600
5147
wpnum = 7
5148
tstart = self.get_sim_time()
5149
while True:
5150
if self.get_sim_time() - tstart > timeout:
5151
raise AutoTestTimeoutException("Did not get wanted current waypoint")
5152
5153
if (self.get_altitude(relative=True, timeout=2) - init_altitude) < -10:
5154
raise NotAchievedException("Descended >10m before reaching desired waypoint,\
5155
indicating slope was not replanned")
5156
5157
seq = self.mav.waypoint_current()
5158
self.progress("Waiting for wp=%u current=%u" % (wpnum, seq))
5159
if seq == wpnum:
5160
break
5161
5162
self.set_current_waypoint(2)
5163
5164
#
5165
# Second run with GLIDE_SLOPE_THR = 0 (no re-plan).
5166
#
5167
self.set_parameter("GLIDE_SLOPE_THR", 0)
5168
5169
# Wait for waypoint commanding rapid descent, followed by climb.
5170
self.wait_current_waypoint(5, timeout=1200)
5171
5172
# This time altitude should descend significantly below the initial altitude
5173
init_altitude = self.get_altitude(relative=True, timeout=2)
5174
timeout = 600
5175
wpnum = 7
5176
tstart = self.get_sim_time()
5177
while True:
5178
if self.get_sim_time() - tstart > timeout:
5179
raise AutoTestTimeoutException("Did not get wanted altitude")
5180
5181
seq = self.mav.waypoint_current()
5182
self.progress("Waiting for wp=%u current=%u" % (wpnum, seq))
5183
if seq == wpnum:
5184
raise NotAchievedException("Reached desired waypoint without first decending 10m,\
5185
indicating slope was replanned unexpectedly")
5186
5187
if (self.get_altitude(relative=True, timeout=2) - init_altitude) < -10:
5188
break
5189
5190
# Disarm
5191
self.wait_disarmed(timeout=600)
5192
5193
self.progress("Mission OK")
5194
5195
def MAV_CMD_NAV_LOITER_TURNS(self, target_system=1, target_component=1):
5196
'''test MAV_CMD_NAV_LOITER_TURNS mission item'''
5197
alt = 100
5198
seq = 0
5199
items = []
5200
tests = [
5201
(self.home_relative_loc_ne(50, -50), 100, 0.3),
5202
(self.home_relative_loc_ne(100, 50), 1005, 3),
5203
]
5204
# add a home position:
5205
items.append(self.mav.mav.mission_item_int_encode(
5206
target_system,
5207
target_component,
5208
seq, # seq
5209
mavutil.mavlink.MAV_FRAME_GLOBAL,
5210
mavutil.mavlink.MAV_CMD_NAV_WAYPOINT,
5211
0, # current
5212
0, # autocontinue
5213
0, # p1
5214
0, # p2
5215
0, # p3
5216
0, # p4
5217
0, # latitude
5218
0, # longitude
5219
0, # altitude
5220
mavutil.mavlink.MAV_MISSION_TYPE_MISSION))
5221
seq += 1
5222
5223
# add takeoff
5224
items.append(self.mav.mav.mission_item_int_encode(
5225
target_system,
5226
target_component,
5227
seq, # seq
5228
mavutil.mavlink.MAV_FRAME_GLOBAL_RELATIVE_ALT,
5229
mavutil.mavlink.MAV_CMD_NAV_TAKEOFF,
5230
0, # current
5231
0, # autocontinue
5232
0, # p1
5233
0, # p2
5234
0, # p3
5235
0, # p4
5236
0, # latitude
5237
0, # longitude
5238
alt, # altitude
5239
mavutil.mavlink.MAV_MISSION_TYPE_MISSION))
5240
seq += 1
5241
5242
# add circles
5243
for (loc, radius, turn) in tests:
5244
items.append(self.mav.mav.mission_item_int_encode(
5245
target_system,
5246
target_component,
5247
seq, # seq
5248
mavutil.mavlink.MAV_FRAME_GLOBAL_RELATIVE_ALT,
5249
mavutil.mavlink.MAV_CMD_NAV_LOITER_TURNS,
5250
0, # current
5251
0, # autocontinue
5252
turn, # p1
5253
0, # p2
5254
radius, # p3
5255
0, # p4
5256
int(loc.lat*1e7), # latitude
5257
int(loc.lng*1e7), # longitude
5258
alt, # altitude
5259
mavutil.mavlink.MAV_MISSION_TYPE_MISSION))
5260
seq += 1
5261
5262
# add an RTL
5263
items.append(self.mav.mav.mission_item_int_encode(
5264
target_system,
5265
target_component,
5266
seq, # seq
5267
mavutil.mavlink.MAV_FRAME_GLOBAL,
5268
mavutil.mavlink.MAV_CMD_NAV_RETURN_TO_LAUNCH,
5269
0, # current
5270
0, # autocontinue
5271
0, # p1
5272
0, # p2
5273
0, # p3
5274
0, # p4
5275
0, # latitude
5276
0, # longitude
5277
0, # altitude
5278
mavutil.mavlink.MAV_MISSION_TYPE_MISSION))
5279
seq += 1
5280
5281
self.upload_using_mission_protocol(mavutil.mavlink.MAV_MISSION_TYPE_MISSION, items)
5282
downloaded_items = self.download_using_mission_protocol(mavutil.mavlink.MAV_MISSION_TYPE_MISSION)
5283
ofs = 2
5284
self.progress("Checking downloaded mission is as expected")
5285
for (loc, radius, turn) in tests:
5286
downloaded = downloaded_items[ofs]
5287
if radius > 255:
5288
# ArduPilot only stores % 10
5289
radius = radius - radius % 10
5290
if downloaded.param3 != radius:
5291
raise NotAchievedException(
5292
"Did not get expected radius for item %u; want=%f got=%f" %
5293
(ofs, radius, downloaded.param3))
5294
if turn > 0 and turn < 1:
5295
# ArduPilot stores fractions in 8 bits (*256) and unpacks it (/256)
5296
turn = int(turn*256) / 256.0
5297
if downloaded.param1 != turn:
5298
raise NotAchievedException(
5299
"Did not get expected turn for item %u; want=%f got=%f" %
5300
(ofs, turn, downloaded.param1))
5301
ofs += 1
5302
5303
self.change_mode('AUTO')
5304
self.wait_ready_to_arm()
5305
self.arm_vehicle()
5306
self.set_parameter("NAVL1_LIM_BANK", 50)
5307
5308
self.wait_current_waypoint(2)
5309
5310
for (loc, expected_radius, _) in tests:
5311
self.wait_circling_point_with_radius(
5312
loc,
5313
expected_radius,
5314
epsilon=20.0,
5315
timeout=240,
5316
)
5317
self.set_current_waypoint(self.current_waypoint()+1)
5318
5319
self.fly_home_land_and_disarm(timeout=180)
5320
5321
def MidAirDisarmDisallowed(self):
5322
'''Ensure mid-air disarm is not possible'''
5323
self.takeoff(50)
5324
disarmed = False
5325
try:
5326
self.disarm_vehicle()
5327
disarmed = True
5328
except ValueError as e:
5329
self.progress("Got %s" % repr(e))
5330
if "Expected MAV_RESULT_ACCEPTED got MAV_RESULT_FAILED" not in str(e):
5331
raise e
5332
if disarmed:
5333
raise NotAchievedException("Disarmed when we shouldn't have")
5334
# should still be able to force-disarm:
5335
self.disarm_vehicle(force=True)
5336
self.reboot_sitl()
5337
5338
def AerobaticsScripting(self):
5339
'''Fixed Wing Aerobatics'''
5340
applet_script = "Aerobatics/FixedWing/plane_aerobatics.lua"
5341
airshow = "Aerobatics/FixedWing/Schedules/AirShow.txt"
5342
trick72 = "trick72.txt"
5343
5344
model = "plane-3d"
5345
5346
self.customise_SITL_commandline(
5347
[],
5348
model=model,
5349
defaults_filepath="Tools/autotest/models/plane-3d.parm",
5350
wipe=True)
5351
5352
self.context_push()
5353
self.install_applet_script_context(applet_script)
5354
self.install_applet_script_context(airshow, install_name=trick72)
5355
self.context_collect('STATUSTEXT')
5356
self.reboot_sitl()
5357
5358
self.set_parameter("TRIK_ENABLE", 1)
5359
self.set_rc(7, 1000) # disable tricks
5360
5361
self.scripting_restart()
5362
self.wait_text("Enabled 3 aerobatic tricks", check_context=True)
5363
self.set_parameters({
5364
"TRIK1_ID": 72,
5365
"RC7_OPTION" : 300, # activation switch
5366
"RC9_OPTION" : 301, # selection switch
5367
"SIM_SPEEDUP": 5, # need to give some cycles to lua
5368
})
5369
5370
self.wait_ready_to_arm()
5371
self.change_mode("TAKEOFF")
5372
self.arm_vehicle()
5373
self.wait_altitude(30, 40, timeout=30, relative=True)
5374
self.change_mode("CRUISE")
5375
5376
self.set_rc(9, 1000) # select first trick
5377
self.delay_sim_time(1)
5378
self.set_rc(7, 1500) # show selected trick
5379
5380
self.wait_text("Trick 1 selected (SuperAirShow)", check_context=True)
5381
self.set_rc(7, 2000) # activate trick
5382
self.wait_text("Trick 1 started (SuperAirShow)", check_context=True)
5383
5384
highest_error = 0
5385
while True:
5386
m = self.mav.recv_match(type='NAMED_VALUE_FLOAT', blocking=True, timeout=2)
5387
if not m:
5388
break
5389
if m.name != 'PERR':
5390
continue
5391
highest_error = max(highest_error, m.value)
5392
if highest_error > 15:
5393
raise NotAchievedException("path error %.1f" % highest_error)
5394
5395
if highest_error == 0:
5396
raise NotAchievedException("path error not reported")
5397
self.progress("Finished trick, max error=%.1fm" % highest_error)
5398
self.disarm_vehicle(force=True)
5399
5400
messages = self.context_collection('STATUSTEXT')
5401
self.context_pop()
5402
self.reboot_sitl()
5403
5404
# check all messages to see if we got all tricks
5405
tricks = ["Loop", "HalfReverseCubanEight", "ScaleFigureEight", "Immelmann",
5406
"Split-S", "RollingCircle", "HumptyBump", "HalfCubanEight",
5407
"BarrelRoll", "CrossBoxTopHat", "TriangularLoop",
5408
"Finishing SuperAirShow!"]
5409
texts = [m.text for m in messages]
5410
for t in tricks:
5411
if t in texts:
5412
self.progress("Completed trick %s" % t)
5413
else:
5414
raise NotAchievedException("Missing trick %s" % t)
5415
5416
def UniversalAutoLandScript(self):
5417
'''Test UniversalAutoLandScript'''
5418
applet_script = "UniversalAutoLand.lua"
5419
self.customise_SITL_commandline(["--home", "-35.362938,149.165085,585,173"])
5420
5421
self.install_applet_script_context(applet_script)
5422
self.context_collect('STATUSTEXT')
5423
self.set_parameters({
5424
"SCR_ENABLE" : 1,
5425
"SCR_VM_I_COUNT" : 1000000,
5426
"RTL_AUTOLAND" : 2
5427
})
5428
self.reboot_sitl()
5429
self.wait_text("Loaded UniversalAutoLand.lua", check_context=True)
5430
self.set_parameters({
5431
"AUTOLAND_ENABLE" : 1,
5432
"AUTOLAND_WP_ALT" : 55,
5433
"AUTOLAND_WP_DIST" : 400
5434
})
5435
self.scripting_restart()
5436
self.wait_text("Scripting: restarted", check_context=True)
5437
5438
self.wait_ready_to_arm()
5439
self.arm_vehicle()
5440
self.change_mode("AUTO")
5441
self.wait_text("Captured initial takeoff direction", check_context=True)
5442
5443
self.wait_disarmed(120)
5444
self.progress("Check the landed heading matches takeoff")
5445
self.wait_heading(173, accuracy=5, timeout=1)
5446
loc = mavutil.location(-35.362938, 149.165085, 585, 173)
5447
if self.get_distance(loc, self.mav.location()) > 35:
5448
raise NotAchievedException("Did not land close to home")
5449
5450
def SDCardWPTest(self):
5451
'''test BRD_SD_MISSION support'''
5452
spiral_script = "mission_spiral.lua"
5453
5454
self.context_push()
5455
self.install_example_script(spiral_script)
5456
self.context_collect('STATUSTEXT')
5457
self.set_parameters({
5458
"BRD_SD_MISSION" : 64,
5459
"SCR_ENABLE" : 1,
5460
"SCR_VM_I_COUNT" : 1000000
5461
})
5462
5463
self.wait_ready_to_arm()
5464
self.reboot_sitl()
5465
5466
self.wait_text("Loaded spiral mission creator", check_context=True)
5467
self.set_parameters({
5468
"SCR_USER2": 19, # radius
5469
"SCR_USER3": -35.36322, # lat
5470
"SCR_USER4": 149.16525, # lon
5471
"SCR_USER5": 684.13, # alt
5472
})
5473
5474
count = (65536 // 15) - 1
5475
5476
self.progress("Creating spiral mission of size %s" % count)
5477
self.set_parameter("SCR_USER1", count)
5478
5479
self.wait_text("Created spiral of size %u" % count, check_context=True)
5480
5481
self.progress("Checking spiral before reboot")
5482
self.set_parameter("SCR_USER6", count)
5483
self.wait_text("Compared spiral of size %u OK" % count, check_context=True)
5484
self.set_parameter("SCR_USER6", 0)
5485
5486
self.wait_ready_to_arm()
5487
self.reboot_sitl()
5488
self.progress("Checking spiral after reboot")
5489
self.set_parameter("SCR_USER6", count)
5490
self.wait_text("Compared spiral of size %u OK" % count, check_context=True)
5491
5492
self.remove_installed_script(spiral_script)
5493
5494
self.context_pop()
5495
self.wait_ready_to_arm()
5496
self.reboot_sitl()
5497
5498
def MANUAL_CONTROL(self):
5499
'''test MANUAL_CONTROL mavlink message'''
5500
self.set_parameter("SYSID_MYGCS", self.mav.source_system)
5501
5502
self.progress("Takeoff")
5503
self.takeoff(alt=50)
5504
5505
self.change_mode('FBWA')
5506
5507
tstart = self.get_sim_time_cached()
5508
roll_input = -500
5509
want_roll_degrees = -12
5510
while True:
5511
if self.get_sim_time_cached() - tstart > 10:
5512
raise AutoTestTimeoutException("Did not reach roll")
5513
self.progress("Sending roll-left")
5514
self.mav.mav.manual_control_send(
5515
1, # target system
5516
32767, # x (pitch)
5517
roll_input, # y (roll)
5518
32767, # z (thrust)
5519
32767, # r (yaw)
5520
0) # button mask
5521
m = self.mav.recv_match(type='ATTITUDE', blocking=True, timeout=1)
5522
print("m=%s" % str(m))
5523
if m is None:
5524
continue
5525
p = math.degrees(m.roll)
5526
self.progress("roll=%f want<=%f" % (p, want_roll_degrees))
5527
if p <= want_roll_degrees:
5528
break
5529
self.mav.mav.manual_control_send(
5530
1, # target system
5531
32767, # x (pitch)
5532
32767, # y (roll)
5533
32767, # z (thrust)
5534
32767, # r (yaw)
5535
0) # button mask
5536
self.fly_home_land_and_disarm()
5537
5538
def mission_home_point(self, target_system=1, target_component=1):
5539
'''just an empty-ish item-int to store home'''
5540
return self.mav.mav.mission_item_int_encode(
5541
target_system,
5542
target_component,
5543
0, # seq
5544
mavutil.mavlink.MAV_FRAME_GLOBAL,
5545
mavutil.mavlink.MAV_CMD_NAV_WAYPOINT,
5546
0, # current
5547
0, # autocontinue
5548
0, # p1
5549
0, # p2
5550
0, # p3
5551
0, # p4
5552
0, # latitude
5553
0, # longitude
5554
0, # altitude
5555
mavutil.mavlink.MAV_MISSION_TYPE_MISSION)
5556
5557
def mission_jump_tag(self, tag, target_system=1, target_component=1):
5558
'''create a jump tag mission item'''
5559
return self.mav.mav.mission_item_int_encode(
5560
target_system,
5561
target_component,
5562
0, # seq
5563
mavutil.mavlink.MAV_FRAME_GLOBAL,
5564
mavutil.mavlink.MAV_CMD_JUMP_TAG,
5565
0, # current
5566
0, # autocontinue
5567
tag, # p1
5568
0, # p2
5569
0, # p3
5570
0, # p4
5571
0, # latitude
5572
0, # longitude
5573
0, # altitude
5574
mavutil.mavlink.MAV_MISSION_TYPE_MISSION)
5575
5576
def mission_do_jump_tag(self, tag, target_system=1, target_component=1):
5577
'''create a jump tag mission item'''
5578
return self.mav.mav.mission_item_int_encode(
5579
target_system,
5580
target_component,
5581
0, # seq
5582
mavutil.mavlink.MAV_FRAME_GLOBAL,
5583
mavutil.mavlink.MAV_CMD_DO_JUMP_TAG,
5584
0, # current
5585
0, # autocontinue
5586
tag, # p1
5587
0, # p2
5588
0, # p3
5589
0, # p4
5590
0, # latitude
5591
0, # longitude
5592
0, # altitude
5593
mavutil.mavlink.MAV_MISSION_TYPE_MISSION)
5594
5595
def mission_anonymous_waypoint(self, target_system=1, target_component=1):
5596
'''just a boring waypoint'''
5597
return self.mav.mav.mission_item_int_encode(
5598
target_system,
5599
target_component,
5600
0, # seq
5601
mavutil.mavlink.MAV_FRAME_GLOBAL,
5602
mavutil.mavlink.MAV_CMD_NAV_WAYPOINT,
5603
0, # current
5604
0, # autocontinue
5605
0, # p1
5606
0, # p2
5607
0, # p3
5608
0, # p4
5609
1, # latitude
5610
1, # longitude
5611
1, # altitude
5612
mavutil.mavlink.MAV_MISSION_TYPE_MISSION)
5613
5614
def renumber_mission_items(self, mission):
5615
count = 0
5616
for item in mission:
5617
item.seq = count
5618
count += 1
5619
5620
def MissionJumpTags_missing_jump_target(self, target_system=1, target_component=1):
5621
self.start_subtest("Check missing-jump-tag behaviour")
5622
jump_target = 2
5623
mission = [
5624
self.mission_home_point(),
5625
self.mission_anonymous_waypoint(),
5626
self.mission_anonymous_waypoint(),
5627
self.mission_jump_tag(jump_target),
5628
self.mission_anonymous_waypoint(),
5629
self.mission_anonymous_waypoint(),
5630
]
5631
self.renumber_mission_items(mission)
5632
self.check_mission_upload_download(mission)
5633
self.progress("Checking incorrect tag behaviour")
5634
self.run_cmd(
5635
mavutil.mavlink.MAV_CMD_DO_JUMP_TAG,
5636
p1=jump_target + 1,
5637
want_result=mavutil.mavlink.MAV_RESULT_FAILED
5638
)
5639
self.progress("Checking correct tag behaviour")
5640
self.run_cmd(
5641
mavutil.mavlink.MAV_CMD_DO_JUMP_TAG,
5642
p1=jump_target,
5643
)
5644
self.assert_current_waypoint(4)
5645
5646
def MissionJumpTags_do_jump_to_bad_tag(self, target_system=1, target_component=1):
5647
mission = [
5648
self.mission_home_point(),
5649
self.mission_anonymous_waypoint(),
5650
self.mission_do_jump_tag(17),
5651
self.mission_anonymous_waypoint(),
5652
]
5653
self.renumber_mission_items(mission)
5654
self.check_mission_upload_download(mission)
5655
self.change_mode('AUTO')
5656
self.arm_vehicle()
5657
self.set_current_waypoint(2, check_afterwards=False)
5658
self.assert_mode('RTL')
5659
self.disarm_vehicle()
5660
5661
def MissionJumpTags_jump_tag_at_end_of_mission(self, target_system=1, target_component=1):
5662
mission = [
5663
self.mission_home_point(),
5664
self.mission_anonymous_waypoint(),
5665
self.mission_jump_tag(17),
5666
]
5667
# Jumping to an end of a mission, either DO_JUMP or DO_JUMP_TAG will result in a failed attempt.
5668
# The failure is from mission::set_current_cmd() returning false if it can not find any NAV
5669
# commands on or after the index. Two scenarios:
5670
# 1) AUTO mission triggered: The the set_command will fail and it will cause an RTL event
5671
# (Harder to test, need vehicle to actually reach the waypoint)
5672
# 2) GCS/MAVLink: It will return MAV_RESULT_FAILED and there's on change to the mission. (Easy to test)
5673
self.renumber_mission_items(mission)
5674
self.check_mission_upload_download(mission)
5675
self.progress("Checking correct tag behaviour")
5676
self.change_mode('AUTO')
5677
self.arm_vehicle()
5678
self.run_cmd(
5679
mavutil.mavlink.MAV_CMD_DO_JUMP_TAG,
5680
p1=17,
5681
want_result=mavutil.mavlink.MAV_RESULT_FAILED
5682
)
5683
self.disarm_vehicle()
5684
5685
def MissionJumpTags(self):
5686
'''test MAV_CMD_JUMP_TAG'''
5687
self.wait_ready_to_arm()
5688
self.MissionJumpTags_missing_jump_target()
5689
self.MissionJumpTags_do_jump_to_bad_tag()
5690
self.MissionJumpTags_jump_tag_at_end_of_mission()
5691
5692
def AltResetBadGPS(self):
5693
'''Tests the handling of poor GPS lock pre-arm alt resets'''
5694
self.set_parameters({
5695
"SIM_GPS1_GLTCH_Z": 0,
5696
"SIM_GPS1_ACC": 0.3,
5697
})
5698
self.wait_ready_to_arm()
5699
5700
m = self.assert_receive_message('GLOBAL_POSITION_INT')
5701
relalt = m.relative_alt*0.001
5702
if abs(relalt) > 3:
5703
raise NotAchievedException("Bad relative alt %.1f" % relalt)
5704
5705
self.progress("Setting low accuracy, glitching GPS")
5706
self.set_parameter("SIM_GPS1_ACC", 40)
5707
self.set_parameter("SIM_GPS1_GLTCH_Z", -47)
5708
5709
self.progress("Waiting 10s for height update")
5710
self.delay_sim_time(10)
5711
5712
self.wait_ready_to_arm()
5713
self.arm_vehicle()
5714
5715
m = self.assert_receive_message('GLOBAL_POSITION_INT')
5716
relalt = m.relative_alt*0.001
5717
if abs(relalt) > 3:
5718
raise NotAchievedException("Bad glitching relative alt %.1f" % relalt)
5719
5720
self.disarm_vehicle()
5721
# reboot to clear potentially bad state
5722
5723
def trigger_airspeed_cal(self):
5724
self.run_cmd(
5725
mavutil.mavlink.MAV_CMD_PREFLIGHT_CALIBRATION,
5726
p3=1,
5727
)
5728
5729
def AirspeedCal(self):
5730
'''test Airspeed calibration'''
5731
5732
self.start_subtest('1 airspeed sensor')
5733
self.context_push()
5734
self.context_collect('STATUSTEXT')
5735
self.trigger_airspeed_cal()
5736
self.wait_statustext('Airspeed 1 calibrated', check_context=True)
5737
self.context_pop()
5738
5739
self.context_push()
5740
self.context_collect('STATUSTEXT')
5741
self.start_subtest('0 airspeed sensors')
5742
self.set_parameter('ARSPD_TYPE', 0)
5743
self.reboot_sitl()
5744
self.wait_statustext('No airspeed sensor', check_context=True)
5745
self.trigger_airspeed_cal()
5746
self.delay_sim_time(5)
5747
if self.statustext_in_collections('Airspeed 1 calibrated'):
5748
raise NotAchievedException("Did not disable airspeed sensor?!")
5749
self.context_pop()
5750
5751
self.start_subtest('2 airspeed sensors')
5752
self.set_parameter('ARSPD_TYPE', 100)
5753
self.set_parameter('ARSPD2_TYPE', 100)
5754
self.reboot_sitl()
5755
self.context_push()
5756
self.context_collect('STATUSTEXT')
5757
self.trigger_airspeed_cal()
5758
self.wait_statustext('Airspeed 1 calibrated', check_context=True)
5759
self.wait_statustext('Airspeed 2 calibrated', check_context=True)
5760
self.context_pop()
5761
5762
self.reboot_sitl()
5763
5764
def RunMissionScript(self):
5765
'''Test run_mission.py script'''
5766
script = os.path.join('Tools', 'autotest', 'run_mission.py')
5767
self.stop_SITL()
5768
util.run_cmd([
5769
util.reltopdir(script),
5770
self.binary,
5771
'plane',
5772
self.generic_mission_filepath_for_filename("flaps.txt"),
5773
])
5774
self.start_SITL()
5775
5776
def MAV_CMD_GUIDED_CHANGE_ALTITUDE(self):
5777
'''test handling of MAV_CMD_GUIDED_CHANGE_ALTITUDE'''
5778
self.install_terrain_handlers_context()
5779
self.start_subtest("set home relative altitude")
5780
self.takeoff(30, relative=True)
5781
self.change_mode('GUIDED')
5782
for alt in 50, 70:
5783
self.run_cmd_int(
5784
mavutil.mavlink.MAV_CMD_GUIDED_CHANGE_ALTITUDE,
5785
p7=alt,
5786
frame=mavutil.mavlink.MAV_FRAME_GLOBAL_RELATIVE_ALT,
5787
)
5788
self.wait_altitude(alt-1, alt+1, timeout=30, relative=True)
5789
5790
# test for #24535
5791
self.start_subtest("switch to loiter and resume guided maintains home relative altitude")
5792
self.change_mode('LOITER')
5793
self.delay_sim_time(5)
5794
self.change_mode('GUIDED')
5795
self.wait_altitude(
5796
alt-3, # NOTE: reuse of alt from above loop!
5797
alt+3,
5798
minimum_duration=10,
5799
timeout=30,
5800
relative=True,
5801
)
5802
# test that this works if switching between RELATIVE (HOME) and GLOBAL(AMSL)
5803
self.start_subtest("set global/AMSL altitude, switch to loiter and resume guided")
5804
alt = 625
5805
self.run_cmd_int(
5806
mavutil.mavlink.MAV_CMD_GUIDED_CHANGE_ALTITUDE,
5807
p7=alt,
5808
frame=mavutil.mavlink.MAV_FRAME_GLOBAL,
5809
)
5810
self.wait_altitude(alt-3, alt+3, timeout=30, relative=False)
5811
self.change_mode('LOITER')
5812
self.delay_sim_time(5)
5813
self.change_mode('GUIDED')
5814
self.wait_altitude(
5815
alt-5, # NOTE: reuse of alt from above CHANGE_ALTITUDE
5816
alt+5,
5817
minimum_duration=10,
5818
timeout=30,
5819
relative=False,
5820
)
5821
# test that this works if switching between RELATIVE (HOME) and terrain
5822
self.start_subtest("set terrain altitude, switch to loiter and resume guided")
5823
self.change_mode('GUIDED')
5824
alt = 100
5825
self.run_cmd_int(
5826
mavutil.mavlink.MAV_CMD_GUIDED_CHANGE_ALTITUDE,
5827
p7=alt,
5828
frame=mavutil.mavlink.MAV_FRAME_GLOBAL_TERRAIN_ALT,
5829
)
5830
self.wait_altitude(
5831
alt-5, # NOTE: reuse of alt from abovE
5832
alt+5, # use a 5m buffer as the plane needs to go up and down a bit to maintain terrain distance
5833
minimum_duration=10,
5834
timeout=30,
5835
relative=False,
5836
altitude_source="TERRAIN_REPORT.current_height"
5837
)
5838
self.change_mode('LOITER')
5839
self.delay_sim_time(5)
5840
self.change_mode('GUIDED')
5841
self.wait_altitude(
5842
alt-5, # NOTE: reuse of alt from abovE
5843
alt+5, # use a 5m buffer as the plane needs to go up and down a bit to maintain terrain distance
5844
minimum_duration=10,
5845
timeout=30,
5846
relative=False,
5847
altitude_source="TERRAIN_REPORT.current_height"
5848
)
5849
5850
self.delay_sim_time(5)
5851
self.fly_home_land_and_disarm()
5852
5853
def _MAV_CMD_PREFLIGHT_CALIBRATION(self, command):
5854
self.context_push()
5855
self.start_subtest("Denied when armed")
5856
self.wait_ready_to_arm()
5857
self.arm_vehicle()
5858
command(
5859
mavutil.mavlink.MAV_CMD_PREFLIGHT_CALIBRATION,
5860
p1=1,
5861
want_result=mavutil.mavlink.MAV_RESULT_FAILED,
5862
)
5863
self.disarm_vehicle()
5864
5865
self.context_collect('STATUSTEXT')
5866
5867
self.start_subtest("gyro cal")
5868
command(
5869
mavutil.mavlink.MAV_CMD_PREFLIGHT_CALIBRATION,
5870
p1=1,
5871
)
5872
5873
self.start_subtest("baro cal")
5874
command(
5875
mavutil.mavlink.MAV_CMD_PREFLIGHT_CALIBRATION,
5876
p3=1,
5877
)
5878
self.wait_statustext('Barometer calibration complete', check_context=True)
5879
5880
# accelcal skipped here, it is checked elsewhere
5881
5882
self.start_subtest("ins trim")
5883
command(
5884
mavutil.mavlink.MAV_CMD_PREFLIGHT_CALIBRATION,
5885
p5=2,
5886
)
5887
5888
# enforced delay between cals:
5889
self.delay_sim_time(5)
5890
5891
self.start_subtest("simple accel cal")
5892
command(
5893
mavutil.mavlink.MAV_CMD_PREFLIGHT_CALIBRATION,
5894
p5=4,
5895
)
5896
# simple gyro cal makes the GPS units go unhealthy as they are
5897
# not maintaining their update rate (gyro cal is synchronous
5898
# in the main loop). Usually ~30 seconds to recover...
5899
self.wait_gps_sys_status_not_present_or_enabled_and_healthy(timeout=60)
5900
5901
self.start_subtest("force save accels")
5902
command(
5903
mavutil.mavlink.MAV_CMD_PREFLIGHT_CALIBRATION,
5904
p5=76,
5905
)
5906
5907
self.start_subtest("force save compasses")
5908
command(
5909
mavutil.mavlink.MAV_CMD_PREFLIGHT_CALIBRATION,
5910
p2=76,
5911
)
5912
5913
self.context_pop()
5914
5915
def MAV_CMD_PREFLIGHT_CALIBRATION(self):
5916
'''test MAV_CMD_PREFLIGHT_CALIBRATION mavlink handling'''
5917
self._MAV_CMD_PREFLIGHT_CALIBRATION(self.run_cmd)
5918
self._MAV_CMD_PREFLIGHT_CALIBRATION(self.run_cmd_int)
5919
5920
def MAV_CMD_DO_INVERTED_FLIGHT(self):
5921
'''fly upside-down mission item'''
5922
alt = 30
5923
wps = self.create_simple_relhome_mission([
5924
(mavutil.mavlink.MAV_CMD_NAV_TAKEOFF, 0, 0, alt),
5925
(mavutil.mavlink.MAV_CMD_NAV_WAYPOINT, 400, 0, alt),
5926
self.create_MISSION_ITEM_INT(
5927
mavutil.mavlink.MAV_CMD_DO_INVERTED_FLIGHT,
5928
p1=1,
5929
),
5930
(mavutil.mavlink.MAV_CMD_NAV_WAYPOINT, 800, 0, alt),
5931
self.create_MISSION_ITEM_INT(
5932
mavutil.mavlink.MAV_CMD_DO_INVERTED_FLIGHT,
5933
p1=0,
5934
),
5935
(mavutil.mavlink.MAV_CMD_NAV_WAYPOINT, 1200, 0, alt),
5936
(mavutil.mavlink.MAV_CMD_NAV_RETURN_TO_LAUNCH, 0, 0, 0),
5937
])
5938
self.check_mission_upload_download(wps)
5939
5940
self.change_mode('AUTO')
5941
self.wait_ready_to_arm()
5942
5943
self.arm_vehicle()
5944
5945
self.wait_current_waypoint(2) # upright flight
5946
self.wait_message_field_values("NAV_CONTROLLER_OUTPUT", {
5947
"nav_roll": 0,
5948
"nav_pitch": 0,
5949
}, epsilon=10)
5950
5951
def check_altitude(mav, m):
5952
global initial_airspeed_threshold_reached
5953
m_type = m.get_type()
5954
if m_type != 'GLOBAL_POSITION_INT':
5955
return
5956
if abs(30 - m.relative_alt * 0.001) > 15:
5957
raise NotAchievedException("Bad altitude while flying inverted")
5958
5959
self.context_push()
5960
self.install_message_hook_context(check_altitude)
5961
5962
self.wait_current_waypoint(4) # inverted flight
5963
self.wait_message_field_values("NAV_CONTROLLER_OUTPUT", {
5964
"nav_roll": 180,
5965
"nav_pitch": 9,
5966
}, epsilon=10,)
5967
5968
self.wait_current_waypoint(6) # upright flight
5969
self.wait_message_field_values("NAV_CONTROLLER_OUTPUT", {
5970
"nav_roll": 0,
5971
"nav_pitch": 0,
5972
}, epsilon=10)
5973
5974
self.context_pop() # remove the check_altitude call
5975
5976
self.wait_current_waypoint(7)
5977
5978
self.fly_home_land_and_disarm()
5979
5980
def MAV_CMD_DO_AUTOTUNE_ENABLE(self):
5981
'''test enabling autotune via mavlink'''
5982
self.context_collect('STATUSTEXT')
5983
self.run_cmd(mavutil.mavlink.MAV_CMD_DO_AUTOTUNE_ENABLE, p1=1)
5984
self.wait_statustext('Started autotune', check_context=True)
5985
self.run_cmd_int(mavutil.mavlink.MAV_CMD_DO_AUTOTUNE_ENABLE, p1=0)
5986
self.wait_statustext('Stopped autotune', check_context=True)
5987
5988
def DO_PARACHUTE(self):
5989
'''test triggering parachute via mavlink'''
5990
self.set_parameters({
5991
"CHUTE_ENABLED": 1,
5992
"CHUTE_TYPE": 10,
5993
"SERVO9_FUNCTION": 27,
5994
"SIM_PARA_ENABLE": 1,
5995
"SIM_PARA_PIN": 9,
5996
"FS_LONG_ACTN": 3,
5997
})
5998
for command in self.run_cmd, self.run_cmd_int:
5999
self.wait_servo_channel_value(9, 1100)
6000
self.wait_ready_to_arm()
6001
self.arm_vehicle()
6002
command(
6003
mavutil.mavlink.MAV_CMD_DO_PARACHUTE,
6004
p1=mavutil.mavlink.PARACHUTE_RELEASE,
6005
)
6006
self.wait_servo_channel_value(9, 1300)
6007
self.disarm_vehicle()
6008
self.reboot_sitl()
6009
6010
def _MAV_CMD_DO_GO_AROUND(self, command):
6011
self.load_mission("mission.txt")
6012
self.set_parameter("RTL_AUTOLAND", 3)
6013
self.change_mode('AUTO')
6014
self.wait_ready_to_arm()
6015
self.arm_vehicle()
6016
self.wait_current_waypoint(6)
6017
command(mavutil.mavlink.MAV_CMD_DO_GO_AROUND, p1=150)
6018
self.wait_current_waypoint(5)
6019
self.wait_altitude(135, 165, relative=True)
6020
self.wait_disarmed(timeout=300)
6021
6022
def MAV_CMD_DO_GO_AROUND(self):
6023
'''test MAV_CMD_DO_GO_AROUND as a mavlink command'''
6024
self._MAV_CMD_DO_GO_AROUND(self.run_cmd)
6025
self._MAV_CMD_DO_GO_AROUND(self.run_cmd_int)
6026
6027
def _MAV_CMD_DO_FLIGHTTERMINATION(self, command):
6028
self.set_parameters({
6029
"AFS_ENABLE": 1,
6030
"SYSID_MYGCS": self.mav.source_system,
6031
"AFS_TERM_ACTION": 42,
6032
})
6033
self.wait_ready_to_arm()
6034
self.arm_vehicle()
6035
self.context_collect('STATUSTEXT')
6036
command(mavutil.mavlink.MAV_CMD_DO_FLIGHTTERMINATION, p1=1)
6037
self.wait_disarmed()
6038
self.wait_text('Terminating due to GCS request', check_context=True)
6039
self.reboot_sitl()
6040
6041
def MAV_CMD_DO_FLIGHTTERMINATION(self):
6042
'''test MAV_CMD_DO_FLIGHTTERMINATION works on Plane'''
6043
self._MAV_CMD_DO_FLIGHTTERMINATION(self.run_cmd)
6044
self._MAV_CMD_DO_FLIGHTTERMINATION(self.run_cmd_int)
6045
6046
def MAV_CMD_DO_LAND_START(self):
6047
'''test MAV_CMD_DO_LAND_START as mavlink command'''
6048
self.set_parameters({
6049
"RTL_AUTOLAND": 3,
6050
})
6051
self.upload_simple_relhome_mission([
6052
(mavutil.mavlink.MAV_CMD_NAV_TAKEOFF, 0, 0, 30),
6053
(mavutil.mavlink.MAV_CMD_NAV_LOITER_UNLIM, 0, 0, 30),
6054
self.create_MISSION_ITEM_INT(
6055
mavutil.mavlink.MAV_CMD_DO_LAND_START,
6056
),
6057
(mavutil.mavlink.MAV_CMD_NAV_WAYPOINT, 800, 0, 0),
6058
])
6059
6060
self.change_mode('AUTO')
6061
self.wait_ready_to_arm()
6062
6063
self.arm_vehicle()
6064
6065
self.start_subtest("DO_LAND_START as COMMAND_LONG")
6066
self.wait_current_waypoint(2)
6067
self.run_cmd(mavutil.mavlink.MAV_CMD_DO_LAND_START)
6068
self.wait_current_waypoint(4)
6069
6070
self.start_subtest("DO_LAND_START as COMMAND_INT")
6071
self.set_current_waypoint(2)
6072
self.run_cmd_int(mavutil.mavlink.MAV_CMD_DO_LAND_START)
6073
self.wait_current_waypoint(4)
6074
6075
self.fly_home_land_and_disarm()
6076
6077
def MAV_CMD_NAV_ALTITUDE_WAIT(self):
6078
'''test MAV_CMD_NAV_ALTITUDE_WAIT mission item, wiggling only'''
6079
6080
# Load a single waypoint
6081
self.upload_simple_relhome_mission([
6082
self.create_MISSION_ITEM_INT(
6083
mavutil.mavlink.MAV_CMD_NAV_ALTITUDE_WAIT,
6084
p1=1000, # 1000m alt threshold, this should not trigger
6085
p2=10, # 10m/s descent rate, this should not trigger
6086
p3=10 # servo wiggle every 10 seconds
6087
)
6088
])
6089
6090
# Set initial conditions for servo wiggle testing
6091
servo_wiggled = {1: False, 2: False, 4: False}
6092
6093
def look_for_wiggle(mav, m):
6094
if m.get_type() == 'SERVO_OUTPUT_RAW':
6095
# Throttle must be zero
6096
if m.servo3_raw != 1000:
6097
raise NotAchievedException(
6098
"Throttle must be 0 in altitude wait, got %f" % m.servo3_raw)
6099
6100
# Check if all servos wiggle
6101
if m.servo1_raw != 1500:
6102
servo_wiggled[1] = True
6103
if m.servo2_raw != 1500:
6104
servo_wiggled[2] = True
6105
if m.servo4_raw != 1500:
6106
servo_wiggled[4] = True
6107
6108
# Start mission
6109
self.change_mode('AUTO')
6110
self.wait_ready_to_arm()
6111
self.arm_vehicle()
6112
6113
# Check outputs
6114
self.context_push()
6115
self.install_message_hook_context(look_for_wiggle)
6116
6117
# Wait for a bit to let message hook sample
6118
self.delay_sim_time(60)
6119
6120
self.context_pop()
6121
6122
# If the mission item completes as there is no other waypoints we will end up in RTL
6123
if not self.mode_is('AUTO'):
6124
raise NotAchievedException("Must still be in AUTO")
6125
6126
# Raise error if not all servos have wiggled
6127
if not all(servo_wiggled.values()):
6128
raise NotAchievedException("Not all servos have moved within the test frame")
6129
6130
self.disarm_vehicle()
6131
6132
def start_flying_simple_rehome_mission(self, items):
6133
'''uploads items, changes mode to auto, waits ready to arm and arms
6134
vehicle. If the first item it a takeoff you can expect the
6135
vehicle to fly after this method returns
6136
'''
6137
6138
self.upload_simple_relhome_mission(items)
6139
6140
self.change_mode('AUTO')
6141
self.wait_ready_to_arm()
6142
6143
self.arm_vehicle()
6144
6145
def InteractTest(self):
6146
'''just takeoff'''
6147
6148
if self.mavproxy is None:
6149
raise NotAchievedException("Must be started with --map")
6150
6151
self.start_flying_simple_rehome_mission([
6152
(mavutil.mavlink.MAV_CMD_NAV_TAKEOFF, 0, 0, 30),
6153
(mavutil.mavlink.MAV_CMD_NAV_WAYPOINT, 800, 0, 0),
6154
(mavutil.mavlink.MAV_CMD_NAV_WAYPOINT, 800, 800, 0),
6155
(mavutil.mavlink.MAV_CMD_NAV_WAYPOINT, 0, 400, 0),
6156
])
6157
6158
self.wait_current_waypoint(4)
6159
6160
self.set_parameter('SIM_SPEEDUP', 1)
6161
6162
self.mavproxy.interact()
6163
6164
def MAV_CMD_MISSION_START(self):
6165
'''test MAV_CMD_MISSION_START starts AUTO'''
6166
self.upload_simple_relhome_mission([
6167
(mavutil.mavlink.MAV_CMD_NAV_WAYPOINT, 800, 0, 0),
6168
])
6169
for run_cmd in self.run_cmd, self.run_cmd_int:
6170
self.change_mode('LOITER')
6171
run_cmd(mavutil.mavlink.MAV_CMD_MISSION_START)
6172
self.wait_mode('AUTO')
6173
6174
def MAV_CMD_NAV_LOITER_UNLIM(self):
6175
'''test receiving MAV_CMD_NAV_LOITER_UNLIM from GCS'''
6176
self.takeoff(10)
6177
self.run_cmd(mavutil.mavlink.MAV_CMD_NAV_LOITER_UNLIM)
6178
self.wait_mode('LOITER')
6179
self.change_mode('GUIDED')
6180
self.run_cmd_int(mavutil.mavlink.MAV_CMD_NAV_LOITER_UNLIM)
6181
self.wait_mode('LOITER')
6182
self.fly_home_land_and_disarm()
6183
6184
def MAV_CMD_NAV_RETURN_TO_LAUNCH(self):
6185
'''test receiving MAV_CMD_NAV_RETURN_TO_LAUNCH from GCS'''
6186
self.set_parameter('RTL_AUTOLAND', 1)
6187
self.start_flying_simple_rehome_mission([
6188
(mavutil.mavlink.MAV_CMD_NAV_TAKEOFF, 0, 0, 30),
6189
(mavutil.mavlink.MAV_CMD_NAV_LOITER_UNLIM, 0, 0, 30),
6190
self.create_MISSION_ITEM_INT(
6191
mavutil.mavlink.MAV_CMD_DO_LAND_START,
6192
),
6193
(mavutil.mavlink.MAV_CMD_NAV_WAYPOINT, 800, 0, 0),
6194
])
6195
6196
for i in self.run_cmd, self.run_cmd_int:
6197
self.wait_current_waypoint(2)
6198
self.run_cmd(mavutil.mavlink.MAV_CMD_NAV_RETURN_TO_LAUNCH)
6199
self.wait_current_waypoint(4)
6200
self.set_current_waypoint(2)
6201
self.fly_home_land_and_disarm()
6202
6203
def location_from_ADSB_VEHICLE(self, m):
6204
'''return a mavutil.location extracted from an ADSB_VEHICLE mavlink
6205
message'''
6206
if m.altitude_type != mavutil.mavlink.ADSB_ALTITUDE_TYPE_GEOMETRIC:
6207
raise ValueError("Expected geometric alt")
6208
return mavutil.location(
6209
m.lat*1e-7,
6210
m.lon*1e-7,
6211
m.altitude/1000.0585, # mm -> m
6212
m.heading * 0.01 # centidegrees -> degrees
6213
)
6214
6215
def SagetechMXS(self):
6216
'''test Sagetech MXS ADSB device driver'''
6217
sim_name = "sagetech_mxs"
6218
self.set_parameters({
6219
"SERIAL5_PROTOCOL": 35,
6220
"ADSB_TYPE": 4, # Sagetech-MXS
6221
"SIM_ADSB_TYPES": 8, # Sagetech-MXS
6222
"SIM_ADSB_COUNT": 5,
6223
})
6224
self.customise_SITL_commandline(["--serial5=sim:%s" % sim_name])
6225
m = self.assert_receive_message("ADSB_VEHICLE")
6226
adsb_vehicle_loc = self.location_from_ADSB_VEHICLE(m)
6227
self.progress("ADSB Vehicle at loc %s" % str(adsb_vehicle_loc))
6228
home = self.home_position_as_mav_location()
6229
self.assert_distance(home, adsb_vehicle_loc, 0, 10000)
6230
6231
def MinThrottle(self):
6232
'''Make sure min throttle does not apply in manual mode and does in FBWA'''
6233
6234
servo_min = self.get_parameter("RC3_MIN")
6235
servo_max = self.get_parameter("RC3_MAX")
6236
min_throttle = 10
6237
servo_min_throttle = servo_min + (servo_max - servo_min) * (min_throttle / 100)
6238
6239
# Set min throttle
6240
self.set_parameter('THR_MIN', min_throttle)
6241
6242
# Should be 0 throttle while disarmed
6243
self.change_mode("MANUAL")
6244
self.drain_mav() # make sure we have the latest data before checking throttle output
6245
self.assert_servo_channel_value(3, servo_min)
6246
6247
# Arm and check throttle is still 0 in manual
6248
self.wait_ready_to_arm()
6249
self.arm_vehicle()
6250
self.drain_mav()
6251
self.assert_servo_channel_value(3, servo_min)
6252
6253
# FBWA should apply throttle min
6254
self.change_mode("FBWA")
6255
self.drain_mav()
6256
self.assert_servo_channel_value(3, servo_min_throttle)
6257
6258
# But not when disarmed
6259
self.disarm_vehicle()
6260
self.drain_mav()
6261
self.assert_servo_channel_value(3, servo_min)
6262
6263
def ClimbThrottleSaturation(self):
6264
'''check what happens when throttle is saturated in GUIDED'''
6265
self.set_parameters({
6266
"TECS_CLMB_MAX": 30,
6267
"TKOFF_ALT": 1000,
6268
})
6269
6270
self.change_mode("TAKEOFF")
6271
self.wait_ready_to_arm()
6272
self.arm_vehicle()
6273
self.wait_message_field_values('VFR_HUD', {
6274
"throttle": 100,
6275
}, minimum_duration=30, timeout=90)
6276
self.disarm_vehicle(force=True)
6277
self.reboot_sitl()
6278
6279
def GuidedAttitudeNoGPS(self):
6280
'''test that guided-attitude still works with no GPS'''
6281
self.takeoff(50)
6282
self.change_mode('GUIDED')
6283
self.context_push()
6284
self.set_parameter('SIM_GPS1_ENABLE', 0)
6285
self.delay_sim_time(30)
6286
self.set_attitude_target()
6287
self.context_pop()
6288
self.fly_home_land_and_disarm()
6289
6290
def ScriptStats(self):
6291
'''test script stats logging'''
6292
self.context_push()
6293
self.set_parameters({
6294
'SCR_ENABLE': 1,
6295
'SCR_DEBUG_OPTS': 8, # runtime memory usage and time
6296
})
6297
self.install_test_scripts_context([
6298
"math.lua",
6299
"strings.lua",
6300
])
6301
self.install_example_script_context('simple_loop.lua')
6302
self.context_collect('STATUSTEXT')
6303
6304
self.reboot_sitl()
6305
6306
self.wait_statustext('hello, world')
6307
delay = 20
6308
self.delay_sim_time(delay, reason='gather some stats')
6309
self.wait_statustext("math.lua exceeded time limit", check_context=True, timeout=0)
6310
6311
dfreader = self.dfreader_for_current_onboard_log()
6312
seen_hello_world = False
6313
# runtime = None
6314
while True:
6315
m = dfreader.recv_match(type=['SCR'])
6316
if m is None:
6317
break
6318
if m.Name == "simple_loop.lua":
6319
seen_hello_world = True
6320
# if m.Name == "math.lua":
6321
# runtime = m.Runtime
6322
6323
if not seen_hello_world:
6324
raise NotAchievedException("Did not see simple_loop.lua script")
6325
6326
# self.progress(f"math took {runtime} seconds to run over {delay} seconds")
6327
# if runtime == 0:
6328
# raise NotAchievedException("Expected non-zero runtime for math")
6329
6330
self.context_pop()
6331
self.reboot_sitl()
6332
6333
def GPSPreArms(self):
6334
'''ensure GPS prearm checks work'''
6335
self.wait_ready_to_arm()
6336
self.start_subtest('DroneCAN sanity checks')
6337
self.set_parameter('GPS1_TYPE', 9)
6338
self.set_parameter('GPS2_TYPE', 9)
6339
self.set_parameter('GPS1_CAN_OVRIDE', 130)
6340
self.set_parameter('GPS2_CAN_OVRIDE', 130)
6341
self.assert_prearm_failure(
6342
"set for multiple GPS",
6343
other_prearm_failures_fatal=False,
6344
)
6345
6346
def SetHomeAltChange(self):
6347
'''check modes retain altitude when home alt changed'''
6348
for mode in 'FBWB', 'CRUISE', 'LOITER':
6349
self.set_rc(3, 1000)
6350
self.wait_ready_to_arm()
6351
home = self.home_position_as_mav_location()
6352
target_alt = 20
6353
self.takeoff(target_alt, mode="TAKEOFF")
6354
self.delay_sim_time(20) # Give some time to altitude to stabilize.
6355
self.set_rc(3, 1500)
6356
self.change_mode(mode)
6357
higher_home = copy.copy(home)
6358
higher_home.alt += 40
6359
self.set_home(higher_home)
6360
self.wait_altitude(home.alt+target_alt-5, home.alt+target_alt+5, relative=False, minimum_duration=10, timeout=11)
6361
self.disarm_vehicle(force=True)
6362
self.reboot_sitl()
6363
6364
def SetHomeAltChange2(self):
6365
'''ensure TECS operates predictably as home altitude changes continuously'''
6366
'''
6367
This can happen when performing a ship landing, where the home
6368
coordinates are continuously set by the ship GNSS RX.
6369
'''
6370
self.set_parameter('TRIM_THROTTLE', 70)
6371
self.wait_ready_to_arm()
6372
home = self.home_position_as_mav_location()
6373
target_alt = 20
6374
self.takeoff(target_alt, mode="TAKEOFF")
6375
self.change_mode("LOITER")
6376
self.delay_sim_time(20) # Let the plane settle.
6377
6378
tstart = self.get_sim_time()
6379
test_time = 10 # Run the test for 10s.
6380
pub_freq = 10
6381
for i in range(test_time*pub_freq):
6382
tnow = self.get_sim_time()
6383
higher_home = copy.copy(home)
6384
# Produce 1Hz sine waves in home altitude change.
6385
higher_home.alt += 40*math.sin((tnow-tstart)*(2*math.pi))
6386
self.set_home(higher_home)
6387
if tnow-tstart > test_time:
6388
break
6389
self.delay_sim_time(1.0/pub_freq)
6390
6391
# Test if the altitude is still within bounds.
6392
self.wait_altitude(home.alt+target_alt-5, home.alt+target_alt+5, relative=False, minimum_duration=1, timeout=2)
6393
self.disarm_vehicle(force=True)
6394
self.reboot_sitl()
6395
6396
def SetHomeAltChange3(self):
6397
'''same as SetHomeAltChange, but the home alt change occurs during TECS operation'''
6398
self.wait_ready_to_arm()
6399
home = self.home_position_as_mav_location()
6400
target_alt = 20
6401
self.takeoff(target_alt, mode="TAKEOFF")
6402
self.change_mode("LOITER")
6403
self.delay_sim_time(20) # Let the plane settle.
6404
6405
higher_home = copy.copy(home)
6406
higher_home.alt += 40
6407
self.set_home(higher_home)
6408
self.wait_altitude(home.alt+target_alt-5, home.alt+target_alt+5, relative=False, minimum_duration=10, timeout=10.1)
6409
6410
self.disarm_vehicle(force=True)
6411
self.reboot_sitl()
6412
6413
def ForceArm(self):
6414
'''check force-arming functionality'''
6415
self.set_parameter("SIM_GPS1_ENABLE", 0)
6416
# 21196 is the mavlink standard, 2989 is legacy
6417
for magic_value in 21196, 2989:
6418
self.wait_sensor_state(mavutil.mavlink.MAV_SYS_STATUS_PREARM_CHECK,
6419
present=True,
6420
enabled=True,
6421
healthy=False,
6422
)
6423
self.run_cmd(
6424
mavutil.mavlink.MAV_CMD_COMPONENT_ARM_DISARM,
6425
p1=1, # ARM
6426
p2=0,
6427
want_result=mavutil.mavlink.MAV_RESULT_FAILED,
6428
)
6429
self.run_cmd(
6430
mavutil.mavlink.MAV_CMD_COMPONENT_ARM_DISARM,
6431
p1=1, # ARM
6432
p2=magic_value,
6433
want_result=mavutil.mavlink.MAV_RESULT_ACCEPTED,
6434
)
6435
self.disarm_vehicle()
6436
6437
def _MAV_CMD_EXTERNAL_WIND_ESTIMATE(self, command):
6438
self.reboot_sitl()
6439
6440
def cmp_with_variance(a, b, p):
6441
return abs(a - b) < p
6442
6443
def check_eq(speed, direction, ret_dir, timeout=1):
6444
command(mavutil.mavlink.MAV_CMD_EXTERNAL_WIND_ESTIMATE, p1=speed, p3=direction)
6445
6446
tstart = self.get_sim_time()
6447
while True:
6448
if self.get_sim_time_cached() - tstart > timeout:
6449
raise NotAchievedException(
6450
f"Failed to set wind speed or/and direction: speed != {speed} or direction != {direction}")
6451
6452
m = self.assert_receive_message("WIND")
6453
if cmp_with_variance(m.speed, speed, 0.5) and cmp_with_variance(m.direction, ret_dir, 5):
6454
return True
6455
6456
check_eq(1, 45, 45)
6457
check_eq(2, 90, 90)
6458
check_eq(3, 120, 120)
6459
check_eq(4, 180, -180)
6460
check_eq(5, 240, -120)
6461
check_eq(6, 320, -40)
6462
check_eq(7, 360, 0)
6463
6464
command(mavutil.mavlink.MAV_CMD_EXTERNAL_WIND_ESTIMATE, p1=-2, p3=90, want_result=mavutil.mavlink.MAV_RESULT_DENIED)
6465
command(mavutil.mavlink.MAV_CMD_EXTERNAL_WIND_ESTIMATE, p1=2, p3=-90, want_result=mavutil.mavlink.MAV_RESULT_DENIED)
6466
command(mavutil.mavlink.MAV_CMD_EXTERNAL_WIND_ESTIMATE, p1=-2, p3=-90, want_result=mavutil.mavlink.MAV_RESULT_DENIED)
6467
command(mavutil.mavlink.MAV_CMD_EXTERNAL_WIND_ESTIMATE, p1=2, p3=370, want_result=mavutil.mavlink.MAV_RESULT_DENIED)
6468
6469
def MAV_CMD_EXTERNAL_WIND_ESTIMATE(self):
6470
'''test MAV_CMD_EXTERNAL_WIND_ESTIMATE as a mavlink command'''
6471
self._MAV_CMD_EXTERNAL_WIND_ESTIMATE(self.run_cmd)
6472
self._MAV_CMD_EXTERNAL_WIND_ESTIMATE(self.run_cmd_int)
6473
6474
def GliderPullup(self):
6475
'''test pullup of glider after ALTITUDE_WAIT'''
6476
self.start_subtest("test glider pullup")
6477
6478
self.customise_SITL_commandline(
6479
[],
6480
model="glider",
6481
defaults_filepath="Tools/autotest/default_params/glider.parm",
6482
wipe=True)
6483
6484
self.set_parameter('LOG_DISARMED', 1)
6485
6486
self.set_parameters({
6487
"PUP_ENABLE": 1,
6488
"SERVO6_FUNCTION": 0, # balloon lift
6489
"SERVO10_FUNCTION": 156, # lift release
6490
"EK3_IMU_MASK": 1, # lane switches just make log harder to read
6491
"AHRS_OPTIONS": 4, # don't disable airspeed based on EKF checks
6492
"ARSPD_OPTIONS": 0, # don't disable airspeed
6493
"ARSPD_WIND_GATE": 0,
6494
})
6495
6496
self.set_servo(6, 1000)
6497
6498
self.load_mission("glider-pullup-mission.txt")
6499
self.change_mode("AUTO")
6500
self.wait_ready_to_arm()
6501
self.arm_vehicle()
6502
self.context_collect('STATUSTEXT')
6503
6504
self.progress("Start balloon lift")
6505
self.set_servo(6, 2000)
6506
6507
self.wait_text("Reached altitude", check_context=True, timeout=1000)
6508
self.wait_text("Start pullup airspeed", check_context=True)
6509
self.wait_text("Pullup airspeed", check_context=True)
6510
self.wait_text("Pullup pitch", check_context=True)
6511
self.wait_text("Pullup level", check_context=True)
6512
self.wait_text("Loiter to alt complete", check_context=True, timeout=1000)
6513
self.wait_text("Flare", check_context=True, timeout=400)
6514
self.wait_text("Auto disarmed", check_context=True, timeout=200)
6515
6516
def BadRollChannelDefined(self):
6517
'''ensure we don't die with a bad Roll channel defined'''
6518
self.set_parameter("RCMAP_ROLL", 17)
6519
6520
def MAV_CMD_NAV_LOITER_TO_ALT(self):
6521
'''test loiter to alt mission item'''
6522
self.upload_simple_relhome_mission([
6523
(mavutil.mavlink.MAV_CMD_NAV_TAKEOFF, 0, 0, 30),
6524
(mavutil.mavlink.MAV_CMD_NAV_LOITER_TO_ALT, 0, 0, 500),
6525
(mavutil.mavlink.MAV_CMD_NAV_LOITER_TO_ALT, 0, 0, 100),
6526
(mavutil.mavlink.MAV_CMD_NAV_WAYPOINT, 800, 0, 0),
6527
])
6528
self.change_mode('AUTO')
6529
self.wait_ready_to_arm()
6530
self.arm_vehicle()
6531
self.wait_altitude(450, 475, relative=True, timeout=600)
6532
self.wait_altitude(75, 125, relative=True, timeout=600)
6533
self.wait_current_waypoint(4)
6534
self.fly_home_land_and_disarm()
6535
6536
def VolzMission(self):
6537
'''test Volz serially-connected servos in a mission'''
6538
volz_motor_mask = ((1 << 0) | (1 << 1) | (1 << 3) | (1 << 8) | (1 << 9) | (1 << 11))
6539
self.set_parameters({
6540
'SERIAL5_PROTOCOL': 14,
6541
'SERVO_VOLZ_MASK': volz_motor_mask,
6542
'RTL_AUTOLAND': 2,
6543
6544
'SIM_VOLZ_ENA': 1,
6545
'SIM_VOLZ_MASK': volz_motor_mask,
6546
})
6547
# defaults file not working?
6548
self.set_parameters({
6549
"SERVO2_REVERSED": 0, # elevator
6550
6551
"SERVO9_FUNCTION": 4,
6552
6553
"SERVO10_FUNCTION": 19, # elevator
6554
6555
"SERVO12_FUNCTION": 21, # rudder
6556
"SERVO12_REVERSED": 1, # rudder
6557
6558
})
6559
self.customise_SITL_commandline([
6560
"--serial5=sim:volz",
6561
], model="plane-redundant",
6562
)
6563
self.wait_ready_to_arm()
6564
self.arm_vehicle()
6565
self.takeoff()
6566
self.fly_home_land_and_disarm()
6567
6568
def Volz(self):
6569
'''test Volz serially-connected'''
6570
volz_motor_mask = ((1 << 0) | (1 << 1) | (1 << 3) | (1 << 8) | (1 << 9) | (1 << 11))
6571
self.set_parameters({
6572
'SERIAL5_PROTOCOL': 14,
6573
'SERVO_VOLZ_MASK': volz_motor_mask,
6574
'RTL_AUTOLAND': 2,
6575
6576
'SIM_VOLZ_ENA': 1,
6577
'SIM_VOLZ_MASK': volz_motor_mask,
6578
})
6579
# defaults file not working?
6580
self.set_parameters({
6581
"SERVO2_REVERSED": 0, # elevator
6582
6583
"SERVO9_FUNCTION": 4,
6584
6585
"SERVO10_FUNCTION": 19, # elevator
6586
6587
"SERVO12_FUNCTION": 21, # rudder
6588
"SERVO12_REVERSED": 1, # rudder
6589
6590
})
6591
self.customise_SITL_commandline([
6592
"--serial5=sim:volz",
6593
], model="plane-redundant",
6594
)
6595
self.wait_ready_to_arm()
6596
self.takeoff()
6597
self.change_mode('FBWA')
6598
straight_and_level_text = "straight-and-level"
6599
self.send_statustext(straight_and_level_text)
6600
self.delay_sim_time(2)
6601
self.progress("sticking servo with constant deflection")
6602
self.set_rc(1, 1400)
6603
self.change_mode('MANUAL')
6604
self.delay_sim_time(0.5)
6605
self.progress("Failing servo")
6606
self.set_parameter('SIM_VOLZ_FMASK', 1)
6607
self.change_mode('FBWA')
6608
aileron_failed_text = "aileron has been failed"
6609
self.send_statustext(aileron_failed_text)
6610
self.delay_sim_time(5)
6611
self.set_parameter('SIM_VOLZ_FMASK', 0)
6612
6613
log_filepath = self.current_onboard_log_filepath()
6614
self.fly_home_land_and_disarm()
6615
6616
self.progress("Inspecting DFReader to ensure servo failure is recorded in the log")
6617
dfreader = self.dfreader_for_path(log_filepath)
6618
while True:
6619
m = dfreader.recv_match(type=['MSG'])
6620
if m is None:
6621
raise NotAchievedException("Did not see straight_and_level_text")
6622
if m.Message == "SRC=250/250:" + straight_and_level_text:
6623
break
6624
6625
self.progress("Ensuring deflections are close to zero in straight-and-level flight")
6626
chan1_good = False
6627
chan9_good = False
6628
while not (chan1_good and chan9_good):
6629
m = dfreader.recv_match()
6630
if m is None:
6631
raise NotAchievedException("Did not see chan1 and chan9 as close-to-0")
6632
if m.get_type() != 'CSRV':
6633
continue
6634
if m.Id == 0 and abs(m.Pos) < 3:
6635
chan1_good = True
6636
elif m.Id == 8 and abs(m.Pos) < 3:
6637
chan9_good = True
6638
6639
while True:
6640
m = dfreader.recv_match(type=['MSG'])
6641
if m is None:
6642
raise NotAchievedException("Did not see aileron_failed_text")
6643
if m.Message == "SRC=250/250:" + aileron_failed_text:
6644
break
6645
6646
self.progress("Checking servo9 is deflected")
6647
while True:
6648
# m = dfreader.recv_match(type=['CSRV'])
6649
m = dfreader.recv_match()
6650
if m is None:
6651
raise NotAchievedException("Did not see chan9 deflection")
6652
if m.get_type() != 'CSRV':
6653
continue
6654
if m.Id != 8:
6655
continue
6656
if m.Pos < 20:
6657
continue
6658
break
6659
6660
self.progress("Ensuring the vehicle stabilised with a single aileron")
6661
while True:
6662
m = dfreader.recv_match()
6663
if m is None:
6664
raise NotAchievedException("Did not see good attitude")
6665
if m.get_type() != 'ATT':
6666
continue
6667
if abs(m.Roll) < 5:
6668
break
6669
6670
self.progress("Ensure the roll integrator is wound up")
6671
while True:
6672
m = dfreader.recv_match()
6673
if m is None:
6674
raise NotAchievedException("Did not see wound-up roll integrator")
6675
if m.get_type() != 'PIDR':
6676
continue
6677
if m.I > 5:
6678
break
6679
6680
self.progress("Checking that aileron is stuck at some deflection")
6681
while True:
6682
m = dfreader.recv_match()
6683
if m is None:
6684
raise NotAchievedException("Did not see csrv Pos/PosCmd discrepency")
6685
if m.get_type() != 'CSRV':
6686
continue
6687
if m.Id != 1:
6688
continue
6689
if abs(m.Pos - m.PosCmd) > 20:
6690
break
6691
6692
def tests(self):
6693
'''return list of all tests'''
6694
ret = []
6695
ret.extend(self.tests1a())
6696
ret.extend(self.tests1b())
6697
return ret
6698
6699
def tests1a(self):
6700
ret = []
6701
ret = super(AutoTestPlane, self).tests()
6702
ret.extend([
6703
self.AuxModeSwitch,
6704
self.TestRCCamera,
6705
self.TestRCRelay,
6706
self.ThrottleFailsafe,
6707
self.NeedEKFToArm,
6708
self.ThrottleFailsafeFence,
6709
self.TestFlaps,
6710
self.DO_CHANGE_SPEED,
6711
self.DO_REPOSITION,
6712
self.GuidedRequest,
6713
self.MainFlight,
6714
self.TestGripperMission,
6715
self.Parachute,
6716
self.ParachuteSinkRate,
6717
self.DO_PARACHUTE,
6718
self.PitotBlockage,
6719
self.AIRSPEED_AUTOCAL,
6720
self.RangeFinder,
6721
self.FenceStatic,
6722
self.FenceRTL,
6723
self.FenceRTLRally,
6724
self.FenceRetRally,
6725
self.FenceAltCeilFloor,
6726
self.FenceMinAltAutoEnable,
6727
self.FenceMinAltEnableAutoland,
6728
self.FenceMinAltAutoEnableAbort,
6729
self.FenceAutoEnableDisableSwitch,
6730
Test(self.FenceCircleExclusionAutoEnable, speedup=20),
6731
self.FenceEnableDisableSwitch,
6732
self.FenceEnableDisableAux,
6733
self.FenceBreachedChangeMode,
6734
self.FenceNoFenceReturnPoint,
6735
self.FenceNoFenceReturnPointInclusion,
6736
self.FenceDisableUnderAction,
6737
self.ADSBFailActionRTL,
6738
self.ADSBResumeActionResumeLoiter,
6739
self.SimADSB,
6740
self.Button,
6741
self.FRSkySPort,
6742
self.FRSkyPassThroughStatustext,
6743
self.FRSkyPassThroughSensorIDs,
6744
self.FRSkyMAVlite,
6745
self.FRSkyD,
6746
self.LTM,
6747
self.DEVO,
6748
self.AdvancedFailsafe,
6749
self.LOITER,
6750
self.MAV_CMD_NAV_LOITER_TURNS,
6751
self.MAV_CMD_NAV_LOITER_TO_ALT,
6752
self.DeepStall,
6753
self.WatchdogHome,
6754
self.LargeMissions,
6755
self.Soaring,
6756
self.Terrain,
6757
self.TerrainMission,
6758
self.UniversalAutoLandScript,
6759
])
6760
return ret
6761
6762
def tests1b(self):
6763
return [
6764
self.TerrainLoiter,
6765
self.VectorNavEAHRS,
6766
self.MicroStrainEAHRS5,
6767
self.MicroStrainEAHRS7,
6768
self.InertialLabsEAHRS,
6769
self.GpsSensorPreArmEAHRS,
6770
self.Deadreckoning,
6771
self.DeadreckoningNoAirSpeed,
6772
self.EKFlaneswitch,
6773
self.AirspeedDrivers,
6774
self.RTL_CLIMB_MIN,
6775
self.ClimbBeforeTurn,
6776
self.IMUTempCal,
6777
self.MAV_CMD_DO_AUX_FUNCTION,
6778
self.SmartBattery,
6779
self.FlyEachFrame,
6780
self.AutoLandMode,
6781
self.RCDisableAirspeedUse,
6782
self.AHRS_ORIENTATION,
6783
self.AHRSTrim,
6784
self.LandingDrift,
6785
self.TakeoffAuto1,
6786
self.TakeoffAuto2,
6787
self.TakeoffAuto3,
6788
self.TakeoffAuto4,
6789
self.TakeoffTakeoff1,
6790
self.TakeoffTakeoff2,
6791
self.TakeoffTakeoff3,
6792
self.TakeoffTakeoff4,
6793
self.TakeoffTakeoff5,
6794
self.TakeoffGround,
6795
self.TakeoffIdleThrottle,
6796
self.TakeoffBadLevelOff,
6797
self.ForcedDCM,
6798
self.DCMFallback,
6799
self.MAVFTP,
6800
self.AUTOTUNE,
6801
self.AutotuneFiltering,
6802
self.MegaSquirt,
6803
self.Hirth,
6804
self.MSP_DJI,
6805
self.SpeedToFly,
6806
self.GlideSlopeThresh,
6807
self.HIGH_LATENCY2,
6808
self.MidAirDisarmDisallowed,
6809
self.AerobaticsScripting,
6810
self.MANUAL_CONTROL,
6811
self.RunMissionScript,
6812
self.WindEstimates,
6813
self.AltResetBadGPS,
6814
self.AirspeedCal,
6815
self.MissionJumpTags,
6816
Test(self.GCSFailsafe, speedup=8),
6817
self.SDCardWPTest,
6818
self.NoArmWithoutMissionItems,
6819
self.MODE_SWITCH_RESET,
6820
self.ExternalPositionEstimate,
6821
self.SagetechMXS,
6822
self.MAV_CMD_GUIDED_CHANGE_ALTITUDE,
6823
self.MAV_CMD_PREFLIGHT_CALIBRATION,
6824
self.MAV_CMD_DO_INVERTED_FLIGHT,
6825
self.MAV_CMD_DO_AUTOTUNE_ENABLE,
6826
self.MAV_CMD_DO_GO_AROUND,
6827
self.MAV_CMD_DO_FLIGHTTERMINATION,
6828
self.MAV_CMD_DO_LAND_START,
6829
self.MAV_CMD_NAV_ALTITUDE_WAIT,
6830
self.InteractTest,
6831
self.MAV_CMD_MISSION_START,
6832
self.TerrainRally,
6833
self.MAV_CMD_NAV_LOITER_UNLIM,
6834
self.MAV_CMD_NAV_RETURN_TO_LAUNCH,
6835
self.MinThrottle,
6836
self.ClimbThrottleSaturation,
6837
self.GuidedAttitudeNoGPS,
6838
self.ScriptStats,
6839
self.GPSPreArms,
6840
self.SetHomeAltChange,
6841
self.SetHomeAltChange2,
6842
self.SetHomeAltChange3,
6843
self.ForceArm,
6844
self.MAV_CMD_EXTERNAL_WIND_ESTIMATE,
6845
self.GliderPullup,
6846
self.BadRollChannelDefined,
6847
self.VolzMission,
6848
self.Volz,
6849
]
6850
6851
def disabled_tests(self):
6852
return {
6853
"LandingDrift": "Flapping test. See https://github.com/ArduPilot/ardupilot/issues/20054",
6854
"InteractTest": "requires user interaction",
6855
"ClimbThrottleSaturation": "requires https://github.com/ArduPilot/ardupilot/pull/27106 to pass",
6856
}
6857
6858
6859
class AutoTestPlaneTests1a(AutoTestPlane):
6860
def tests(self):
6861
return self.tests1a()
6862
6863
6864
class AutoTestPlaneTests1b(AutoTestPlane):
6865
def tests(self):
6866
return self.tests1b()
6867
6868