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/sim_vehicle.py
Views: 1798
1
#!/usr/bin/env python3
2
3
"""
4
Framework to start a simulated vehicle and connect it to MAVProxy.
5
6
Peter Barker, April 2016
7
based on sim_vehicle.sh by Andrew Tridgell, October 2011
8
9
AP_FLAKE8_CLEAN
10
11
"""
12
from __future__ import print_function
13
14
import atexit
15
import datetime
16
import errno
17
import optparse
18
import os
19
import os.path
20
import re
21
import signal
22
import subprocess
23
import sys
24
import tempfile
25
import textwrap
26
import time
27
import shlex
28
import binascii
29
import math
30
31
from pysim import util
32
from pysim import vehicleinfo
33
34
35
# List of open terminal windows for macosx
36
windowID = []
37
38
autotest_dir = os.path.dirname(os.path.realpath(__file__))
39
root_dir = os.path.realpath(os.path.join(autotest_dir, '../..'))
40
41
try:
42
from pymavlink import mavextra
43
except ImportError:
44
sys.path.append(os.path.join(root_dir, "modules/mavlink"))
45
from pymavlink import mavextra
46
47
os.environ["SIM_VEHICLE_SESSION"] = binascii.hexlify(os.urandom(8)).decode()
48
49
50
class CompatError(Exception):
51
"""A custom exception class to hold state if we encounter the parse
52
error we are looking for"""
53
def __init__(self, error, opts, rargs):
54
Exception.__init__(self, error)
55
self.opts = opts
56
self.rargs = rargs
57
58
59
class CompatOptionParser(optparse.OptionParser):
60
"""An option parser which emulates the behaviour of the old
61
sim_vehicle.sh; if passed -C, the first argument not understood starts
62
a list of arguments that are passed straight to mavproxy
63
"""
64
65
class CustomFormatter(optparse.IndentedHelpFormatter):
66
def __init__(self, *args, **kwargs):
67
optparse.IndentedHelpFormatter.__init__(self, *args, **kwargs)
68
69
# taken and modified from from optparse.py's format_option
70
def format_option_preserve_nl(self, option):
71
# The help for each option consists of two parts:
72
# * the opt strings and metavars
73
# eg. ("-x", or "-fFILENAME, --file=FILENAME")
74
# * the user-supplied help string
75
# eg. ("turn on expert mode", "read data from FILENAME")
76
#
77
# If possible, we write both of these on the same line:
78
# -x turn on expert mode
79
#
80
# But if the opt string list is too long, we put the help
81
# string on a second line, indented to the same column it would
82
# start in if it fit on the first line.
83
# -fFILENAME, --file=FILENAME
84
# read data from FILENAME
85
result = []
86
opts = self.option_strings[option]
87
opt_width = self.help_position - self.current_indent - 2
88
if len(opts) > opt_width:
89
opts = "%*s%s\n" % (self.current_indent, "", opts)
90
else: # start help on same line as opts
91
opts = "%*s%-*s " % (self.current_indent, "", opt_width, opts)
92
result.append(opts)
93
if option.help:
94
help_text = self.expand_default(option)
95
tw = textwrap.TextWrapper(replace_whitespace=False,
96
initial_indent="",
97
subsequent_indent=" ",
98
width=self.help_width)
99
100
for line in help_text.split("\n"):
101
help_lines = tw.wrap(line)
102
for wline in help_lines:
103
result.extend(["%*s%s\n" % (self.help_position,
104
"",
105
wline)])
106
elif opts[-1] != "\n":
107
result.append("\n")
108
return "".join(result)
109
110
def format_option(self, option):
111
if str(option).find('frame') != -1:
112
return self.format_option_preserve_nl(option)
113
return optparse.IndentedHelpFormatter.format_option(self, option)
114
115
def __init__(self, *args, **kwargs):
116
formatter = CompatOptionParser.CustomFormatter()
117
optparse.OptionParser.__init__(self,
118
*args,
119
formatter=formatter,
120
**kwargs)
121
122
def error(self, error):
123
"""Override default error handler called by
124
optparse.OptionParser.parse_args when a parse error occurs;
125
raise a detailed exception which can be caught
126
"""
127
if error.find("no such option") != -1:
128
raise CompatError(error, self.values, self.rargs)
129
130
optparse.OptionParser.error(self, error)
131
132
def parse_args(self, args=None, values=None):
133
'''Wrap parse_args so we can catch the exception raised upon
134
discovering the known parameter parsing error
135
'''
136
137
try:
138
opts, args = optparse.OptionParser.parse_args(self)
139
except CompatError as e:
140
if not e.opts.sim_vehicle_sh_compatible:
141
print(e)
142
print("Perhaps you want --sim_vehicle_sh_compatible (-C)?")
143
sys.exit(1)
144
if e.opts.mavproxy_args:
145
print("--mavproxy-args not permitted in compat mode")
146
sys.exit(1)
147
148
args = []
149
opts = e.opts
150
mavproxy_args = [str(e)[16:]] # this trims "no such option" off
151
mavproxy_args.extend(e.rargs)
152
opts.ensure_value("mavproxy_args", " ".join(mavproxy_args))
153
154
return opts, args
155
156
157
def cygwin_pidof(proc_name):
158
""" Thanks to kata198 for this:
159
https://github.com/kata198/cygwin-ps-misc/blob/master/pidof
160
"""
161
pipe = subprocess.Popen("ps -ea | grep " + proc_name,
162
shell=True,
163
stdout=subprocess.PIPE)
164
output_lines = pipe.stdout.read().decode('utf-8').replace("\r", "").split("\n")
165
ret = pipe.wait()
166
pids = []
167
if ret != 0:
168
# No results
169
return []
170
for line in output_lines:
171
if not line:
172
continue
173
line_split = [item for item in line.split(' ') if item]
174
cmd = line_split[-1].split('/')[-1]
175
if cmd == proc_name:
176
try:
177
pid = int(line_split[0].strip())
178
except Exception:
179
pid = int(line_split[1].strip())
180
if pid not in pids:
181
pids.append(pid)
182
return pids
183
184
185
def under_cygwin():
186
"""Return if Cygwin binary exist"""
187
return os.path.exists("/usr/bin/cygstart")
188
189
190
def under_macos():
191
return sys.platform == 'darwin'
192
193
194
def under_vagrant():
195
return os.path.isfile("/ardupilot.vagrant")
196
197
198
def under_wsl2():
199
import platform
200
return 'microsoft-standard-WSL2' in platform.release()
201
202
203
def wsl2_host_ip():
204
if not under_wsl2():
205
return None
206
207
pipe = subprocess.Popen("ip route show default | awk '{print $3}'",
208
shell=True,
209
stdout=subprocess.PIPE)
210
output_lines = pipe.stdout.read().decode('utf-8').strip(' \r\n')
211
ret = pipe.wait()
212
213
if ret != 0:
214
# Command exited with an error. The output it generated probably isn't what we're expecting
215
return None
216
217
if not output_lines:
218
# No output detected, maybe there's no nameserver or WSL2 has some abnormal firewalls/network settings?
219
return None
220
221
return str(output_lines)
222
223
224
def kill_tasks_cygwin(victims):
225
"""Shell out to ps -ea to find processes to kill"""
226
for victim in list(victims):
227
pids = cygwin_pidof(victim)
228
# progress("pids for (%s): %s" %
229
# (victim,",".join([ str(p) for p in pids])))
230
for apid in pids:
231
os.kill(apid, signal.SIGKILL)
232
233
234
def kill_tasks_macos():
235
for window in windowID:
236
cmd = ("osascript -e \'tell application \"Terminal\" to close "
237
"(window(get index of window id %s))\'" % window)
238
os.system(cmd)
239
240
241
def kill_tasks_psutil(victims):
242
"""Use the psutil module to kill tasks by name. Sadly, this module is
243
not available on Windows, but when it is we should be able to *just*
244
use this routine"""
245
import psutil
246
for proc in psutil.process_iter():
247
pdict = proc.as_dict(attrs=['environ', 'status'])
248
if pdict['status'] == psutil.STATUS_ZOMBIE:
249
continue
250
if pdict['environ'] is not None:
251
if pdict['environ'].get('SIM_VEHICLE_SESSION') == os.environ['SIM_VEHICLE_SESSION']:
252
proc.kill()
253
254
255
def kill_tasks_pkill(victims):
256
"""Shell out to pkill(1) to kill processed by name"""
257
for victim in victims: # pkill takes a single pattern, so iterate
258
cmd = ["pkill", victim[:15]] # pkill matches only first 15 characters
259
run_cmd_blocking("pkill", cmd, quiet=True)
260
261
262
class BobException(Exception):
263
"""Handle Bob's Exceptions"""
264
pass
265
266
267
def kill_tasks():
268
"""Clean up stray processes by name. This is a shotgun approach"""
269
progress("Killing tasks")
270
271
if cmd_opts.coverage:
272
import psutil
273
for proc in psutil.process_iter(['pid', 'name', 'environ']):
274
if proc.name() not in ["arducopter", "ardurover", "arduplane", "ardusub", "antennatracker"]:
275
# only kill vehicle that way
276
continue
277
if os.environ['SIM_VEHICLE_SESSION'] not in proc.environ().get('SIM_VEHICLE_SESSION'):
278
# only kill vehicle launched with sim_vehicle.py that way
279
continue
280
proc.terminate()
281
progress("Waiting SITL to exit cleanly and write coverage .gcda")
282
try:
283
proc.wait(timeout=30)
284
progress("Done")
285
except psutil.TimeoutExpired:
286
progress("SITL doesn't want to exit cleaning, killing ...")
287
proc.kill()
288
289
try:
290
victim_names = {
291
'JSBSim',
292
'lt-JSBSim',
293
'ArduPlane.elf',
294
'ArduCopter.elf',
295
'ArduSub.elf',
296
'Rover.elf',
297
'AntennaTracker.elf',
298
'JSBSIm.exe',
299
'MAVProxy.exe',
300
'runsim.py',
301
'AntennaTracker.elf',
302
'scrimmage',
303
'ardurover',
304
'arduplane',
305
'arducopter'
306
}
307
for vehicle in vinfo.options:
308
for frame in vinfo.options[vehicle]["frames"]:
309
frame_info = vinfo.options[vehicle]["frames"][frame]
310
if "waf_target" not in frame_info:
311
continue
312
exe_name = os.path.basename(frame_info["waf_target"])
313
victim_names.add(exe_name)
314
315
if under_cygwin():
316
return kill_tasks_cygwin(victim_names)
317
if under_macos() and os.environ.get('DISPLAY'):
318
# use special macos kill routine if Display is on
319
return kill_tasks_macos()
320
321
try:
322
kill_tasks_psutil(victim_names)
323
except ImportError:
324
kill_tasks_pkill(victim_names)
325
except Exception as e:
326
progress("kill_tasks failed: {}".format(str(e)))
327
328
329
def progress(text):
330
"""Display sim_vehicle progress text"""
331
print("SIM_VEHICLE: " + text)
332
333
334
def wait_unlimited():
335
"""Wait until signal received"""
336
while True:
337
time.sleep(600)
338
339
340
vinfo = vehicleinfo.VehicleInfo()
341
342
343
def do_build(opts, frame_options):
344
"""Build sitl using waf"""
345
progress("WAF build")
346
347
old_dir = os.getcwd()
348
os.chdir(root_dir)
349
350
waf_light = os.path.join(root_dir, "modules/waf/waf-light")
351
352
configure_target = frame_options.get('configure_target', 'sitl')
353
354
cmd_configure = [waf_light, "configure", "--board", configure_target]
355
if opts.debug:
356
cmd_configure.append("--debug")
357
358
if opts.coverage:
359
cmd_configure.append("--coverage")
360
361
if opts.enable_onvif and 'antennatracker' in frame_options["waf_target"]:
362
cmd_configure.append("--enable-onvif")
363
364
if opts.OSD:
365
cmd_configure.append("--enable-sfml")
366
cmd_configure.append("--sitl-osd")
367
368
if opts.OSDMSP:
369
cmd_configure.append("--osd")
370
371
if opts.rgbled:
372
cmd_configure.append("--enable-sfml")
373
cmd_configure.append("--sitl-rgbled")
374
375
if opts.tonealarm:
376
cmd_configure.append("--enable-sfml-audio")
377
378
if opts.math_check_indexes:
379
cmd_configure.append("--enable-math-check-indexes")
380
381
if opts.enable_ekf2:
382
cmd_configure.append("--enable-EKF2")
383
384
if opts.disable_ekf3:
385
cmd_configure.append("--disable-EKF3")
386
387
if opts.postype_single:
388
cmd_configure.append("--postype-single")
389
390
if opts.ekf_double:
391
cmd_configure.append("--ekf-double")
392
393
if opts.ekf_single:
394
cmd_configure.append("--ekf-single")
395
396
if opts.force_32bit:
397
cmd_configure.append("--force-32bit")
398
399
if opts.ubsan:
400
cmd_configure.append("--ubsan")
401
402
if opts.ubsan_abort:
403
cmd_configure.append("--ubsan-abort")
404
405
if opts.num_aux_imus:
406
cmd_configure.append("--num-aux-imus=%s" % opts.num_aux_imus)
407
408
for nv in opts.define:
409
cmd_configure.append("--define=%s" % nv)
410
411
if opts.enable_dds:
412
cmd_configure.append("--enable-dds")
413
414
if opts.disable_networking:
415
cmd_configure.append("--disable-networking")
416
417
if opts.enable_ppp:
418
cmd_configure.append("--enable-PPP")
419
420
if opts.enable_networking_tests:
421
cmd_configure.append("--enable-networking-tests")
422
423
pieces = [shlex.split(x) for x in opts.waf_configure_args]
424
for piece in pieces:
425
cmd_configure.extend(piece)
426
427
if not cmd_opts.no_configure:
428
run_cmd_blocking("Configure waf", cmd_configure, check=True)
429
430
if opts.clean:
431
run_cmd_blocking("Building clean", [waf_light, "clean"])
432
433
print(frame_options)
434
cmd_build = [waf_light, "build", "--target", frame_options["waf_target"]]
435
if opts.jobs is not None:
436
cmd_build += ['-j', str(opts.jobs)]
437
pieces = [shlex.split(x) for x in opts.waf_build_args]
438
for piece in pieces:
439
cmd_build.extend(piece)
440
441
_, sts = run_cmd_blocking("Building", cmd_build)
442
443
if sts != 0: # build failed
444
if opts.rebuild_on_failure:
445
progress("Build failed; cleaning and rebuilding")
446
run_cmd_blocking("Building clean", [waf_light, "clean"])
447
448
_, sts = run_cmd_blocking("Building", cmd_build)
449
if sts != 0:
450
progress("Build failed")
451
sys.exit(1)
452
else:
453
progress("Build failed")
454
sys.exit(1)
455
456
os.chdir(old_dir)
457
458
459
def do_build_parameters(vehicle):
460
# build succeeded
461
# now build parameters
462
progress("Building fresh parameter descriptions")
463
param_parse_path = os.path.join(
464
autotest_dir, "param_metadata/param_parse.py")
465
cmd_param_build = ["python", param_parse_path, '--vehicle', vehicle]
466
467
_, sts = run_cmd_blocking("Building fresh params", cmd_param_build)
468
if sts != 0:
469
progress("Parameter build failed")
470
sys.exit(1)
471
472
473
def get_user_locations_path():
474
'''The user locations.txt file is located by default in
475
$XDG_CONFIG_DIR/ardupilot/locations.txt. If $XDG_CONFIG_DIR is
476
not defined, we look in $HOME/.config/ardupilot/locations.txt. If
477
$HOME is not defined, we look in ./.config/ardupilot/locations.txt.'''
478
479
config_dir = os.environ.get(
480
'XDG_CONFIG_DIR',
481
os.path.join(os.environ.get('HOME', '.'), '.config'))
482
483
user_locations_path = os.path.join(
484
config_dir, 'ardupilot', 'locations.txt')
485
486
return user_locations_path
487
488
489
def find_offsets(instances, file_path):
490
offsets = {}
491
swarminit_filepath = os.path.join(autotest_dir, "swarminit.txt")
492
comment_regex = re.compile(r"\s*#.*")
493
for path in [file_path, swarminit_filepath]:
494
if os.path.isfile(path):
495
with open(path, 'r') as fd:
496
for line in fd:
497
line = re.sub(comment_regex, "", line)
498
line = line.rstrip("\n")
499
if len(line) == 0:
500
continue
501
(instance, offset) = line.split("=")
502
instance = (int)(instance)
503
if (instance not in offsets) and (instance in instances):
504
offsets[instance] = [(float)(x) for x in offset.split(",")]
505
continue
506
if len(offsets) == len(instances):
507
return offsets
508
if len(offsets) == len(instances):
509
return offsets
510
for instance in instances:
511
if instance not in offsets:
512
offsets[instance] = [90.0, 20.0 * instance, 0.0, None]
513
return offsets
514
515
516
def find_geocoder_location(locname):
517
'''find a location using geocoder and SRTM'''
518
try:
519
import geocoder
520
except ImportError:
521
print("geocoder not installed")
522
return None
523
j = geocoder.osm(locname)
524
if j is None or not hasattr(j, 'lat') or j.lat is None:
525
print("geocoder failed to find '%s'" % locname)
526
return None
527
lat = j.lat
528
lon = j.lng
529
from MAVProxy.modules.mavproxy_map import srtm
530
downloader = srtm.SRTMDownloader()
531
downloader.loadFileList()
532
start = time.time()
533
alt = None
534
while time.time() - start < 5:
535
tile = downloader.getTile(int(math.floor(lat)), int(math.floor(lon)))
536
if tile:
537
alt = tile.getAltitudeFromLatLon(lat, lon)
538
break
539
if alt is None:
540
print("timed out getting altitude for '%s'" % locname)
541
return None
542
return [lat, lon, alt, 0.0]
543
544
545
def find_location_by_name(locname):
546
"""Search locations.txt for locname, return GPS coords"""
547
locations_userpath = os.environ.get('ARDUPILOT_LOCATIONS',
548
get_user_locations_path())
549
locations_filepath = os.path.join(autotest_dir, "locations.txt")
550
comment_regex = re.compile(r"\s*#.*")
551
for path in [locations_userpath, locations_filepath]:
552
if not os.path.isfile(path):
553
continue
554
with open(path, 'r') as fd:
555
for line in fd:
556
line = re.sub(comment_regex, "", line)
557
line = line.rstrip("\n")
558
if len(line) == 0:
559
continue
560
(name, loc) = line.split("=")
561
if name == locname:
562
return [(float)(x) for x in loc.split(",")]
563
564
# fallback to geocoder if available
565
loc = find_geocoder_location(locname)
566
if loc is None:
567
sys.exit(1)
568
return loc
569
570
571
def find_spawns(loc, offsets):
572
lat, lon, alt, heading = loc
573
spawns = {}
574
for k in offsets:
575
(x, y, z, head) = offsets[k]
576
if head is None:
577
head = heading
578
g = mavextra.gps_offset(lat, lon, x, y)
579
spawns[k] = ",".join([str(g[0]), str(g[1]), str(alt+z), str(head)])
580
return spawns
581
582
583
def progress_cmd(what, cmd):
584
"""Print cmd in a way a user could cut-and-paste to get the same effect"""
585
progress(what)
586
shell_text = "%s" % (" ".join(['"%s"' % x for x in cmd]))
587
progress(shell_text)
588
589
590
def run_cmd_blocking(what, cmd, quiet=False, check=False, **kw):
591
if not quiet:
592
progress_cmd(what, cmd)
593
594
try:
595
p = subprocess.Popen(cmd, **kw)
596
ret = os.waitpid(p.pid, 0)
597
except Exception as e:
598
print("[%s] An exception has occurred with command: '%s'" % (what, (' ').join(cmd)))
599
print(e)
600
sys.exit(1)
601
602
_, sts = ret
603
if check and sts != 0:
604
progress("(%s) exited with code %d" % (what, sts,))
605
sys.exit(1)
606
return ret
607
608
609
def run_in_terminal_window(name, cmd, **kw):
610
611
"""Execute the run_in_terminal_window.sh command for cmd"""
612
global windowID
613
runme = [os.path.join(autotest_dir, "run_in_terminal_window.sh"), name]
614
runme.extend(cmd)
615
progress_cmd("Run " + name, runme)
616
617
if under_macos() and os.environ.get('DISPLAY'):
618
# on MacOS record the window IDs so we can close them later
619
out = subprocess.Popen(runme, stdout=subprocess.PIPE, **kw).communicate()[0]
620
out = out.decode('utf-8')
621
p = re.compile('tab 1 of window id (.*)')
622
623
tstart = time.time()
624
while time.time() - tstart < 5:
625
tabs = p.findall(out)
626
627
if len(tabs) > 0:
628
break
629
630
time.sleep(0.1)
631
# sleep for extra 2 seconds for application to start
632
time.sleep(2)
633
if len(tabs) > 0:
634
windowID.append(tabs[0])
635
else:
636
progress("Cannot find %s process terminal" % name)
637
else:
638
subprocess.Popen(runme, **kw)
639
640
641
tracker_serial0 = None # blemish
642
643
644
def start_antenna_tracker(opts):
645
"""Compile and run the AntennaTracker, add tracker to mavproxy"""
646
647
global tracker_serial0
648
progress("Preparing antenna tracker")
649
tracker_home = find_location_by_name(opts.tracker_location)
650
vehicledir = os.path.join(autotest_dir, "../../" + "AntennaTracker")
651
options = vinfo.options["AntennaTracker"]
652
tracker_default_frame = options["default_frame"]
653
tracker_frame_options = options["frames"][tracker_default_frame]
654
do_build(opts, tracker_frame_options)
655
tracker_instance = 1
656
oldpwd = os.getcwd()
657
os.chdir(vehicledir)
658
tracker_serial0 = "tcp:127.0.0.1:" + str(5760 + 10 * tracker_instance)
659
binary_basedir = "build/sitl"
660
exe = os.path.join(root_dir,
661
binary_basedir,
662
"bin/antennatracker")
663
run_in_terminal_window("AntennaTracker",
664
["nice",
665
exe,
666
"-I" + str(tracker_instance),
667
"--model=tracker",
668
"--home=" + ",".join([str(x) for x in tracker_home])])
669
os.chdir(oldpwd)
670
671
672
def start_CAN_Periph(opts, frame_info):
673
"""Compile and run the sitl_periph"""
674
675
progress("Preparing sitl_periph_universal")
676
options = vinfo.options["sitl_periph_universal"]['frames']['universal']
677
defaults_path = frame_info.get('periph_params_filename', None)
678
if defaults_path is None:
679
defaults_path = options.get('default_params_filename', None)
680
681
if not isinstance(defaults_path, list):
682
defaults_path = [defaults_path]
683
684
# add in path and make a comma separated list
685
defaults_path = ','.join([util.relcurdir(os.path.join(autotest_dir, p)) for p in defaults_path])
686
687
if not cmd_opts.no_rebuild:
688
do_build(opts, options)
689
exe = os.path.join(root_dir, 'build/sitl_periph_universal', 'bin/AP_Periph')
690
cmd = ["nice"]
691
cmd_name = "sitl_periph_universal"
692
if opts.valgrind:
693
cmd_name += " (valgrind)"
694
cmd.append("valgrind")
695
# adding this option allows valgrind to cope with the overload
696
# of operator new
697
cmd.append("--soname-synonyms=somalloc=nouserintercepts")
698
cmd.append("--track-origins=yes")
699
if opts.gdb or opts.gdb_stopped:
700
cmd_name += " (gdb)"
701
cmd.append("gdb")
702
gdb_commands_file = tempfile.NamedTemporaryFile(mode='w', delete=False)
703
atexit.register(os.unlink, gdb_commands_file.name)
704
gdb_commands_file.write("set pagination off\n")
705
if not opts.gdb_stopped:
706
gdb_commands_file.write("r\n")
707
gdb_commands_file.close()
708
cmd.extend(["-x", gdb_commands_file.name])
709
cmd.append("--args")
710
cmd.append(exe)
711
if defaults_path is not None:
712
cmd.append("--defaults")
713
cmd.append(defaults_path)
714
run_in_terminal_window(cmd_name, cmd)
715
716
717
def start_vehicle(binary, opts, stuff, spawns=None):
718
"""Run the ArduPilot binary"""
719
720
cmd_name = opts.vehicle
721
cmd = []
722
if opts.valgrind:
723
cmd_name += " (valgrind)"
724
cmd.append("valgrind")
725
# adding this option allows valgrind to cope with the overload
726
# of operator new
727
cmd.append("--soname-synonyms=somalloc=nouserintercepts")
728
cmd.append("--track-origins=yes")
729
if opts.callgrind:
730
cmd_name += " (callgrind)"
731
cmd.append("valgrind")
732
cmd.append("--tool=callgrind")
733
if opts.gdb or opts.gdb_stopped:
734
cmd_name += " (gdb)"
735
cmd.append("gdb")
736
gdb_commands_file = tempfile.NamedTemporaryFile(mode='w', delete=False)
737
atexit.register(os.unlink, gdb_commands_file.name)
738
739
for breakpoint in opts.breakpoint:
740
gdb_commands_file.write("b %s\n" % (breakpoint,))
741
if opts.disable_breakpoints:
742
gdb_commands_file.write("disable\n")
743
gdb_commands_file.write("set pagination off\n")
744
if not opts.gdb_stopped:
745
gdb_commands_file.write("r\n")
746
gdb_commands_file.close()
747
cmd.extend(["-x", gdb_commands_file.name])
748
cmd.append("--args")
749
elif opts.lldb or opts.lldb_stopped:
750
cmd_name += " (lldb)"
751
cmd.append("lldb")
752
lldb_commands_file = tempfile.NamedTemporaryFile(mode='w', delete=False)
753
atexit.register(os.unlink, lldb_commands_file.name)
754
755
for breakpoint in opts.breakpoint:
756
lldb_commands_file.write("b %s\n" % (breakpoint,))
757
if not opts.lldb_stopped:
758
lldb_commands_file.write("process launch\n")
759
lldb_commands_file.close()
760
cmd.extend(["-s", lldb_commands_file.name])
761
cmd.append("--")
762
if opts.strace:
763
cmd_name += " (strace)"
764
cmd.append("strace")
765
strace_options = ['-o', binary + '.strace', '-s', '8000', '-ttt']
766
cmd.extend(strace_options)
767
768
cmd.append(binary)
769
if opts.wipe_eeprom:
770
cmd.append("-w")
771
cmd.extend(["--model", stuff["model"]])
772
cmd.extend(["--speedup", str(opts.speedup)])
773
if opts.sysid is not None:
774
cmd.extend(["--sysid", str(opts.sysid)])
775
if opts.slave is not None:
776
cmd.extend(["--slave", str(opts.slave)])
777
if opts.enable_fgview:
778
cmd.extend(["--enable-fgview"])
779
if opts.sitl_instance_args:
780
# this could be a lot better:
781
cmd.extend(opts.sitl_instance_args.split(" "))
782
if opts.mavlink_gimbal:
783
cmd.append("--gimbal")
784
path = None
785
if "default_params_filename" in stuff:
786
paths = stuff["default_params_filename"]
787
if not isinstance(paths, list):
788
paths = [paths]
789
paths = [util.relcurdir(os.path.join(autotest_dir, x)) for x in paths]
790
for x in paths:
791
if not os.path.isfile(x):
792
print("The parameter file (%s) does not exist" % (x,))
793
sys.exit(1)
794
path = ",".join(paths)
795
if cmd_opts.count > 1 or opts.auto_sysid:
796
# we are in a subdirectory when using -n
797
path = os.path.join("..", path)
798
progress("Using defaults from (%s)" % (path,))
799
if opts.flash_storage:
800
cmd.append("--set-storage-flash-enabled 1")
801
cmd.append("--set-storage-posix-enabled 0")
802
elif opts.fram_storage:
803
cmd.append("--set-storage-fram-enabled 1")
804
cmd.append("--set-storage-posix-enabled 0")
805
if opts.add_param_file:
806
for file in opts.add_param_file:
807
if not os.path.isfile(file):
808
print("The parameter file (%s) does not exist" %
809
(file,))
810
sys.exit(1)
811
812
if path is not None:
813
path += "," + str(file)
814
else:
815
path = str(file)
816
817
progress("Adding parameters from (%s)" % (str(file),))
818
if opts.OSDMSP:
819
path += "," + os.path.join(root_dir, "libraries/AP_MSP/Tools/osdtest.parm")
820
path += "," + os.path.join(autotest_dir, "default_params/msposd.parm")
821
subprocess.Popen([os.path.join(root_dir, "libraries/AP_MSP/Tools/msposd.py")])
822
823
if path is not None and len(path) > 0:
824
cmd.extend(["--defaults", path])
825
826
if cmd_opts.start_time is not None:
827
# Parse start_time into a double precision number specifying seconds since 1900.
828
try:
829
start_time_UTC = time.mktime(datetime.datetime.strptime(cmd_opts.start_time, '%Y-%m-%d-%H:%M').timetuple())
830
except Exception:
831
print("Incorrect start time format - require YYYY-MM-DD-HH:MM (given %s)" % cmd_opts.start_time)
832
sys.exit(1)
833
834
cmd.append("--start-time=%d" % start_time_UTC)
835
836
cmd.append("--sim-address=%s" % cmd_opts.sim_address)
837
838
old_dir = os.getcwd()
839
for i, i_dir in zip(instances, instance_dir):
840
c = ["-I" + str(i)]
841
if spawns is not None:
842
c.extend(["--home", spawns[i]])
843
if opts.mcast:
844
c.extend(["--serial0", "mcast:"])
845
elif opts.udp:
846
c.extend(["--serial0", "udpclient:127.0.0.1:" + str(5760+i*10)])
847
if opts.auto_sysid:
848
if opts.sysid is not None:
849
raise ValueError("Can't use auto-sysid and sysid together")
850
sysid = i + 1
851
# Take 0-based logging into account
852
if sysid < 1 or sysid > 255:
853
raise ValueError("Invalid system id %d" % sysid)
854
c.extend(["--sysid", str(sysid)])
855
856
os.chdir(i_dir)
857
run_in_terminal_window(cmd_name, cmd + c)
858
os.chdir(old_dir)
859
860
861
def start_mavproxy(opts, stuff):
862
"""Run mavproxy"""
863
# FIXME: would be nice to e.g. "mavproxy.mavproxy(....).run"
864
# rather than shelling out
865
866
extra_cmd = ""
867
cmd = []
868
if under_cygwin():
869
cmd.append("/usr/bin/cygstart")
870
cmd.append("-w")
871
cmd.append("mavproxy.exe")
872
else:
873
cmd.append("mavproxy.py")
874
875
if opts.mcast:
876
cmd.extend(["--master", "mcast:"])
877
878
# returns a valid IP of the host windows computer if we're WSL2.
879
# This is run before the loop so it only runs once
880
wsl2_host_ip_str = wsl2_host_ip()
881
882
for i in instances:
883
if not opts.no_extra_ports:
884
ports = [14550 + 10 * i]
885
for port in ports:
886
if under_vagrant():
887
# We're running inside of a vagrant guest; forward our
888
# mavlink out to the containing host OS
889
cmd.extend(["--out", "10.0.2.2:" + str(port)])
890
elif wsl2_host_ip_str:
891
# We're running WSL2; forward our
892
# mavlink out to the containing host Windows OS
893
cmd.extend(["--out", str(wsl2_host_ip_str) + ":" + str(port)])
894
else:
895
cmd.extend(["--out", "127.0.0.1:" + str(port)])
896
897
if not opts.mcast:
898
if opts.udp:
899
cmd.extend(["--master", ":" + str(5760 + 10 * i)])
900
else:
901
cmd.extend(["--master", "tcp:127.0.0.1:" + str(5760 + 10 * i)])
902
if stuff["sitl-port"] and not opts.no_rcin:
903
cmd.extend(["--sitl", "127.0.0.1:" + str(5501 + 10 * i)])
904
905
if opts.tracker:
906
cmd.extend(["--load-module", "tracker"])
907
global tracker_serial0
908
# tracker_serial0 is set when we start the tracker...
909
extra_cmd += ("module load map;"
910
"tracker set port %s; "
911
"tracker start; "
912
"tracker arm;" % (tracker_serial0,))
913
914
if opts.mavlink_gimbal:
915
cmd.extend(["--load-module", "gimbal"])
916
917
if "extra_mavlink_cmds" in stuff:
918
extra_cmd += " " + stuff["extra_mavlink_cmds"]
919
920
# Parsing the arguments to pass to mavproxy, split args on space
921
# and "=" signs and ignore those signs within quotation marks
922
if opts.mavproxy_args:
923
# It would be great if this could be done with regex
924
mavargs = opts.mavproxy_args.split(" ")
925
# Find the arguments with '=' in them and split them up
926
for i, x in enumerate(mavargs):
927
if '=' in x:
928
mavargs[i] = x.split('=')[0]
929
mavargs.insert(i+1, x.split('=')[1])
930
# Use this flag to tell if parsing character inbetween a pair
931
# of quotation marks
932
inString = False
933
beginStringIndex = []
934
endStringIndex = []
935
# Iterate through the arguments, looking for the arguments
936
# that begin with a quotation mark and the ones that end with
937
# a quotation mark
938
for i, x in enumerate(mavargs):
939
if not inString and x[0] == "\"":
940
beginStringIndex.append(i)
941
mavargs[i] = x[1:]
942
inString = True
943
elif inString and x[-1] == "\"":
944
endStringIndex.append(i)
945
inString = False
946
mavargs[i] = x[:-1]
947
# Replace the list items with one string to be passed into mavproxy
948
for begin, end in zip(beginStringIndex, endStringIndex):
949
replacement = " ".join(mavargs[begin:end+1])
950
mavargs[begin] = replacement
951
mavargs = mavargs[0:begin+1] + mavargs[end+1:]
952
cmd.extend(mavargs)
953
954
# compatibility pass-through parameters (for those that don't want
955
# to use -C :-)
956
for out in opts.out:
957
cmd.extend(['--out', out])
958
if opts.map:
959
cmd.append('--map')
960
if opts.console:
961
cmd.append('--console')
962
if opts.aircraft is not None:
963
cmd.extend(['--aircraft', opts.aircraft])
964
if opts.moddebug:
965
cmd.append('--moddebug=%u' % opts.moddebug)
966
if opts.mavcesium:
967
cmd.extend(["--load-module", "cesium"])
968
969
if opts.fresh_params:
970
# these were built earlier:
971
path = os.path.join(os.getcwd(), "apm.pdef.xml")
972
cmd.extend(['--load-module', 'param:{"xml-filepath":"%s"}' % path])
973
974
if len(extra_cmd):
975
cmd.extend(['--cmd', extra_cmd])
976
977
# add Tools/mavproxy_modules to PYTHONPATH in autotest so we can
978
# find random MAVProxy helper modules like sitl_calibration
979
local_mp_modules_dir = os.path.abspath(
980
os.path.join(__file__, '..', '..', 'mavproxy_modules'))
981
env = dict(os.environ)
982
old = env.get('PYTHONPATH', None)
983
env['PYTHONPATH'] = local_mp_modules_dir
984
if old is not None:
985
env['PYTHONPATH'] += os.path.pathsep + old
986
987
run_cmd_blocking("Run MavProxy", cmd, env=env)
988
progress("MAVProxy exited")
989
990
if opts.gdb:
991
# in the case that MAVProxy exits (as opposed to being
992
# killed), restart it if we are running under GDB. This
993
# allows ArduPilot to stop (eg. via a very early panic call)
994
# and have you debugging session not be killed.
995
while True:
996
progress("Running under GDB; restarting MAVProxy")
997
run_cmd_blocking("Run MavProxy", cmd, env=env)
998
progress("MAVProxy exited; sleeping 10")
999
time.sleep(10)
1000
1001
1002
vehicle_options_string = '|'.join(vinfo.options.keys())
1003
1004
1005
def generate_frame_help():
1006
ret = ""
1007
for vehicle in vinfo.options:
1008
frame_options = sorted(vinfo.options[vehicle]["frames"].keys())
1009
frame_options_string = '|'.join(frame_options)
1010
ret += "%s: %s\n" % (vehicle, frame_options_string)
1011
return ret
1012
1013
1014
# map from some vehicle aliases back to directory names. APMrover2
1015
# was the old name / directory name for Rover.
1016
vehicle_map = {
1017
"APMrover2": "Rover",
1018
"Copter": "ArduCopter",
1019
"Plane": "ArduPlane",
1020
"Sub": "ArduSub",
1021
"Blimp" : "Blimp",
1022
"Rover": "Rover",
1023
}
1024
# add lower-case equivalents too
1025
for k in list(vehicle_map.keys()):
1026
vehicle_map[k.lower()] = vehicle_map[k]
1027
1028
# define and run parser
1029
parser = CompatOptionParser(
1030
"sim_vehicle.py",
1031
epilog=""
1032
"eeprom.bin in the starting directory contains the parameters for your "
1033
"simulated vehicle. Always start from the same directory. It is "
1034
"recommended that you start in the main vehicle directory for the vehicle "
1035
"you are simulating, for example, start in the ArduPlane directory to "
1036
"simulate ArduPlane")
1037
1038
vehicle_choices = list(vinfo.options.keys())
1039
1040
# add vehicle aliases to argument parser options:
1041
for c in vehicle_map.keys():
1042
vehicle_choices.append(c)
1043
1044
parser.add_option("-v", "--vehicle",
1045
type='choice',
1046
default=None,
1047
help="vehicle type (%s)" % vehicle_options_string,
1048
choices=vehicle_choices)
1049
parser.add_option("-f", "--frame", type='string', default=None, help="""set vehicle frame type
1050
1051
%s""" % (generate_frame_help()))
1052
1053
parser.add_option("--vehicle-binary",
1054
default=None,
1055
help="vehicle binary path")
1056
1057
parser.add_option("-C", "--sim_vehicle_sh_compatible",
1058
action='store_true',
1059
default=False,
1060
help="be compatible with the way sim_vehicle.sh works; "
1061
"make this the first option")
1062
1063
group_build = optparse.OptionGroup(parser, "Build options")
1064
group_build.add_option("-N", "--no-rebuild",
1065
action='store_true',
1066
default=False,
1067
help="don't rebuild before starting ardupilot")
1068
group_build.add_option("--no-configure",
1069
action='store_true',
1070
default=False,
1071
help="don't run waf configure before building")
1072
group_build.add_option("-D", "--debug",
1073
action='store_true',
1074
default=False,
1075
help="build with debugging")
1076
group_build.add_option("-c", "--clean",
1077
action='store_true',
1078
default=False,
1079
help="do a make clean before building")
1080
group_build.add_option("-j", "--jobs",
1081
default=None,
1082
type='int',
1083
help="number of processors to use during build "
1084
"(default for waf : number of processor, for make : 1)")
1085
group_build.add_option("-b", "--build-target",
1086
default=None,
1087
type='string',
1088
help="override SITL build target")
1089
group_build.add_option("--enable-math-check-indexes",
1090
default=False,
1091
action="store_true",
1092
dest="math_check_indexes",
1093
help="enable checking of math indexes")
1094
group_build.add_option("", "--force-32bit",
1095
default=False,
1096
action='store_true',
1097
dest="force_32bit",
1098
help="compile sitl using 32-bit")
1099
group_build.add_option("", "--configure-define",
1100
default=[],
1101
action='append',
1102
dest="define",
1103
help="create a preprocessor define")
1104
group_build.add_option("", "--rebuild-on-failure",
1105
dest="rebuild_on_failure",
1106
action='store_true',
1107
default=False,
1108
help="if build fails, do not clean and rebuild")
1109
group_build.add_option("", "--waf-configure-arg",
1110
action="append",
1111
dest="waf_configure_args",
1112
type="string",
1113
default=[],
1114
help="extra arguments to pass to waf in configure step")
1115
group_build.add_option("", "--waf-build-arg",
1116
action="append",
1117
dest="waf_build_args",
1118
type="string",
1119
default=[],
1120
help="extra arguments to pass to waf in its build step")
1121
group_build.add_option("", "--coverage",
1122
action='store_true',
1123
default=False,
1124
help="use coverage build")
1125
group_build.add_option("", "--ubsan",
1126
default=False,
1127
action='store_true',
1128
dest="ubsan",
1129
help="compile sitl with undefined behaviour sanitiser")
1130
group_build.add_option("", "--ubsan-abort",
1131
default=False,
1132
action='store_true',
1133
dest="ubsan_abort",
1134
help="compile sitl with undefined behaviour sanitiser and abort on error")
1135
group_build.add_option("--num-aux-imus",
1136
dest="num_aux_imus",
1137
default=0,
1138
type='int',
1139
help='number of auxiliary IMUs to simulate')
1140
parser.add_option_group(group_build)
1141
1142
group_sim = optparse.OptionGroup(parser, "Simulation options")
1143
group_sim.add_option("-I", "--instance",
1144
default=0,
1145
type='int',
1146
help="instance of simulator")
1147
group_sim.add_option("-n", "--count",
1148
type='int',
1149
default=1,
1150
help="vehicle count; if this is specified, -I is used as a base-value")
1151
group_sim.add_option("-i", "--instances",
1152
default=None,
1153
type='string',
1154
help="a space delimited list of instances to spawn; if specified, overrides -I and -n.")
1155
group_sim.add_option("-V", "--valgrind",
1156
action='store_true',
1157
default=False,
1158
help="enable valgrind for memory access checking (slow!)")
1159
group_sim.add_option("", "--callgrind",
1160
action='store_true',
1161
default=False,
1162
help="enable valgrind for performance analysis (slow!!)")
1163
group_sim.add_option("-T", "--tracker",
1164
action='store_true',
1165
default=False,
1166
help="start an antenna tracker instance")
1167
group_sim.add_option("", "--enable-onvif",
1168
action="store_true",
1169
help="enable onvif camera control sim using AntennaTracker")
1170
group_sim.add_option("", "--can-peripherals",
1171
action='store_true',
1172
default=False,
1173
help="start a DroneCAN peripheral instance")
1174
group_sim.add_option("-A", "--sitl-instance-args",
1175
type='string',
1176
default=None,
1177
help="pass arguments to SITL instance")
1178
group_sim.add_option("-G", "--gdb",
1179
action='store_true',
1180
default=False,
1181
help="use gdb for debugging ardupilot")
1182
group_sim.add_option("-g", "--gdb-stopped",
1183
action='store_true',
1184
default=False,
1185
help="use gdb for debugging ardupilot (no auto-start)")
1186
group_sim.add_option("--lldb",
1187
action='store_true',
1188
default=False,
1189
help="use lldb for debugging ardupilot")
1190
group_sim.add_option("--lldb-stopped",
1191
action='store_true',
1192
default=False,
1193
help="use ldb for debugging ardupilot (no auto-start)")
1194
group_sim.add_option("-d", "--delay-start",
1195
default=0,
1196
type='float',
1197
help="delay start of mavproxy by this number of seconds")
1198
group_sim.add_option("-B", "--breakpoint",
1199
type='string',
1200
action="append",
1201
default=[],
1202
help="add a breakpoint at given location in debugger")
1203
group_sim.add_option("--disable-breakpoints",
1204
default=False,
1205
action='store_true',
1206
help="disable all breakpoints before starting")
1207
group_sim.add_option("-M", "--mavlink-gimbal",
1208
action='store_true',
1209
default=False,
1210
help="enable MAVLink gimbal")
1211
group_sim.add_option("-L", "--location", type='string',
1212
default=None,
1213
help="use start location from "
1214
"Tools/autotest/locations.txt")
1215
group_sim.add_option("-l", "--custom-location",
1216
type='string',
1217
default=None,
1218
help="set custom start location (lat,lon,alt,heading)")
1219
group_sim.add_option("-S", "--speedup",
1220
default=1,
1221
type='int',
1222
help="set simulation speedup (1 for wall clock time)")
1223
group_sim.add_option("-t", "--tracker-location",
1224
default='CMAC_PILOTSBOX',
1225
type='string',
1226
help="set antenna tracker start location")
1227
group_sim.add_option("-w", "--wipe-eeprom",
1228
action='store_true',
1229
default=False, help="wipe EEPROM and reload parameters")
1230
group_sim.add_option("-m", "--mavproxy-args",
1231
default=None,
1232
type='string',
1233
help="additional arguments to pass to mavproxy.py")
1234
group_sim.add_option("", "--scrimmage-args",
1235
default=None,
1236
type='string',
1237
help="arguments used to populate SCRIMMAGE mission (comma-separated). "
1238
"Currently visual_model, motion_model, and terrain are supported. "
1239
"Usage: [instance=]argument=value...")
1240
group_sim.add_option("", "--strace",
1241
action='store_true',
1242
default=False,
1243
help="strace the ArduPilot binary")
1244
group_sim.add_option("", "--model",
1245
type='string',
1246
default=None,
1247
help="Override simulation model to use")
1248
group_sim.add_option("", "--use-dir",
1249
type='string',
1250
default=None,
1251
help="Store SITL state and output in named directory")
1252
group_sim.add_option("", "--no-mavproxy",
1253
action='store_true',
1254
default=False,
1255
help="Don't launch MAVProxy")
1256
group_sim.add_option("", "--fresh-params",
1257
action='store_true',
1258
dest='fresh_params',
1259
default=False,
1260
help="Generate and use local parameter help XML")
1261
group_sim.add_option("", "--mcast",
1262
action="store_true",
1263
default=False,
1264
help="Use multicasting at default 239.255.145.50:14550")
1265
group_sim.add_option("", "--udp",
1266
action="store_true",
1267
default=False,
1268
help="Use UDP on 127.0.0.1:5760")
1269
group_sim.add_option("", "--osd",
1270
action='store_true',
1271
dest='OSD',
1272
default=False,
1273
help="Enable SITL OSD")
1274
group_sim.add_option("", "--osdmsp",
1275
action='store_true',
1276
dest='OSDMSP',
1277
default=False,
1278
help="Enable SITL OSD using MSP")
1279
group_sim.add_option("", "--tonealarm",
1280
action='store_true',
1281
dest='tonealarm',
1282
default=False,
1283
help="Enable SITL ToneAlarm")
1284
group_sim.add_option("", "--rgbled",
1285
action='store_true',
1286
dest='rgbled',
1287
default=False,
1288
help="Enable SITL RGBLed")
1289
group_sim.add_option("", "--add-param-file",
1290
type='string',
1291
action="append",
1292
default=None,
1293
help="Add a parameters file to use")
1294
group_sim.add_option("", "--no-extra-ports",
1295
action='store_true',
1296
dest='no_extra_ports',
1297
default=False,
1298
help="Disable setup of UDP 14550 and 14551 output")
1299
group_sim.add_option("-Z", "--swarm",
1300
type='string',
1301
default=None,
1302
help="Specify path of swarminit.txt for shifting spawn location")
1303
group_sim.add_option("", "--auto-offset-line",
1304
type="string",
1305
default=None,
1306
help="Argument of form BEARING,DISTANCE. When running multiple instances, form a line along bearing with an interval of DISTANCE", # NOQA
1307
)
1308
group_sim.add_option("--flash-storage",
1309
action='store_true',
1310
help="use flash storage emulation")
1311
group_sim.add_option("--fram-storage",
1312
action='store_true',
1313
help="use fram storage emulation")
1314
group_sim.add_option("--enable-ekf2",
1315
action='store_true',
1316
help="disable EKF2 in build")
1317
group_sim.add_option("--disable-ekf3",
1318
action='store_true',
1319
help="disable EKF3 in build")
1320
group_sim.add_option("", "--start-time",
1321
default=None,
1322
type='string',
1323
help="specify simulation start time in format YYYY-MM-DD-HH:MM in your local time zone")
1324
group_sim.add_option("", "--sysid",
1325
type='int',
1326
default=None,
1327
help="Set SYSID_THISMAV")
1328
group_sim.add_option("--postype-single",
1329
action='store_true',
1330
help="force single precision postype_t")
1331
group_sim.add_option("--ekf-double",
1332
action='store_true',
1333
help="use double precision in EKF")
1334
group_sim.add_option("--ekf-single",
1335
action='store_true',
1336
help="use single precision in EKF")
1337
group_sim.add_option("", "--slave",
1338
type='int',
1339
default=0,
1340
help="Set the number of JSON slave")
1341
group_sim.add_option("", "--auto-sysid",
1342
default=False,
1343
action='store_true',
1344
help="Set SYSID_THISMAV based upon instance number")
1345
group_sim.add_option("", "--sim-address",
1346
type=str,
1347
default="127.0.0.1",
1348
help="IP address of the simulator. Defaults to localhost")
1349
group_sim.add_option("--enable-dds", action='store_true',
1350
help="Enable the dds client to connect with ROS2/DDS")
1351
group_sim.add_option("--disable-networking", action='store_true',
1352
help="Disable networking APIs")
1353
group_sim.add_option("--enable-ppp", action='store_true',
1354
help="Enable PPP networking")
1355
group_sim.add_option("--enable-networking-tests", action='store_true',
1356
help="Enable networking tests")
1357
group_sim.add_option("--enable-fgview", action='store_true',
1358
help="Enable FlightGear output")
1359
1360
parser.add_option_group(group_sim)
1361
1362
1363
# special-cased parameters for mavproxy, because some people's fingers
1364
# have long memories, and they don't want to use -C :-)
1365
group = optparse.OptionGroup(parser,
1366
"Compatibility MAVProxy options "
1367
"(consider using --mavproxy-args instead)")
1368
group.add_option("", "--out",
1369
default=[],
1370
type='string',
1371
action="append",
1372
help="create an additional mavlink output")
1373
group.add_option("", "--map",
1374
default=False,
1375
action='store_true',
1376
help="load map module on startup")
1377
group.add_option("", "--mavcesium",
1378
default=False,
1379
action='store_true',
1380
help="load MAVCesium module on startup")
1381
1382
group.add_option("", "--console",
1383
default=False,
1384
action='store_true',
1385
help="load console module on startup")
1386
group.add_option("", "--aircraft",
1387
default=None,
1388
help="store state and logs in named directory")
1389
group.add_option("", "--moddebug",
1390
default=0,
1391
type=int,
1392
help="mavproxy module debug")
1393
group.add_option("", "--no-rcin",
1394
action='store_true',
1395
help="disable mavproxy rcin")
1396
parser.add_option_group(group)
1397
1398
group_completion = optparse.OptionGroup(parser, "Completion helpers")
1399
group_completion.add_option("", "--list-vehicle",
1400
action='store_true',
1401
help="List the vehicles")
1402
group_completion.add_option("", "--list-frame",
1403
type='string',
1404
default=None,
1405
help="List the vehicle frames")
1406
parser.add_option_group(group_completion)
1407
1408
cmd_opts, cmd_args = parser.parse_args()
1409
1410
if cmd_opts.list_vehicle:
1411
print(' '.join(vinfo.options.keys()))
1412
sys.exit(1)
1413
if cmd_opts.list_frame:
1414
frame_options = sorted(vinfo.options[cmd_opts.list_frame]["frames"].keys())
1415
frame_options_string = ' '.join(frame_options)
1416
print(frame_options_string)
1417
sys.exit(1)
1418
1419
# clean up processes at exit:
1420
atexit.register(kill_tasks)
1421
1422
progress("Start")
1423
1424
if cmd_opts.sim_vehicle_sh_compatible and cmd_opts.jobs is None:
1425
cmd_opts.jobs = 1
1426
1427
# validate parameters
1428
if cmd_opts.valgrind and (cmd_opts.gdb or cmd_opts.gdb_stopped or cmd_opts.lldb or cmd_opts.lldb_stopped):
1429
print("May not use valgrind with gdb or lldb")
1430
sys.exit(1)
1431
1432
if cmd_opts.valgrind and cmd_opts.callgrind:
1433
print("May not use valgrind with callgrind")
1434
sys.exit(1)
1435
1436
if cmd_opts.strace and (cmd_opts.gdb or cmd_opts.gdb_stopped or cmd_opts.lldb or cmd_opts.lldb_stopped):
1437
print("May not use strace with gdb or lldb")
1438
sys.exit(1)
1439
1440
if (cmd_opts.gdb or cmd_opts.gdb_stopped) and (cmd_opts.lldb or cmd_opts.lldb_stopped):
1441
print("May not use lldb with gdb")
1442
sys.exit(1)
1443
1444
if cmd_opts.instance < 0:
1445
print("May not specify a negative instance ID")
1446
sys.exit(1)
1447
1448
if cmd_opts.count < 1:
1449
print("May not specify a count less than 1")
1450
sys.exit(1)
1451
1452
if cmd_opts.strace and cmd_opts.valgrind:
1453
print("valgrind and strace almost certainly not a good idea")
1454
1455
if cmd_opts.strace and cmd_opts.callgrind:
1456
print("callgrind and strace almost certainly not a good idea")
1457
1458
if cmd_opts.sysid and cmd_opts.auto_sysid:
1459
print("Cannot use auto-sysid together with sysid")
1460
sys.exit(1)
1461
1462
# magically determine vehicle type (if required):
1463
if cmd_opts.vehicle is None:
1464
cwd = os.getcwd()
1465
cmd_opts.vehicle = os.path.basename(cwd)
1466
1467
if cmd_opts.vehicle not in vinfo.options:
1468
# try in parent directories, useful for having config in subdirectories
1469
cwd = os.getcwd()
1470
while cwd:
1471
bname = os.path.basename(cwd)
1472
if not bname:
1473
break
1474
if bname in vinfo.options:
1475
cmd_opts.vehicle = bname
1476
break
1477
cwd = os.path.dirname(cwd)
1478
1479
if cmd_opts.vehicle in vehicle_map:
1480
cmd_opts.vehicle = vehicle_map[cmd_opts.vehicle]
1481
elif cmd_opts.vehicle.lower() in vehicle_map:
1482
cmd_opts.vehicle = vehicle_map[cmd_opts.vehicle.lower()]
1483
1484
# try to validate vehicle
1485
if cmd_opts.vehicle not in vinfo.options:
1486
progress('''
1487
** Is (%s) really your vehicle type?
1488
Perhaps you could try -v %s
1489
You could also try changing directory to e.g. the ArduCopter subdirectory
1490
''' % (cmd_opts.vehicle, vehicle_options_string))
1491
sys.exit(1)
1492
1493
# determine frame options (e.g. build type might be "sitl")
1494
if cmd_opts.frame is None:
1495
cmd_opts.frame = vinfo.options[cmd_opts.vehicle]["default_frame"]
1496
1497
frame_infos = vinfo.options_for_frame(cmd_opts.frame,
1498
cmd_opts.vehicle,
1499
cmd_opts)
1500
1501
vehicle_dir = os.path.realpath(os.path.join(root_dir, cmd_opts.vehicle))
1502
if not os.path.exists(vehicle_dir):
1503
print("vehicle directory (%s) does not exist" % (vehicle_dir,))
1504
sys.exit(1)
1505
1506
if cmd_opts.instances is not None:
1507
instances = set()
1508
for i in cmd_opts.instances.split(' '):
1509
i = (int)(i)
1510
if i < 0:
1511
print("May not specify a negative instance ID")
1512
sys.exit(1)
1513
instances.add(i)
1514
instances = sorted(instances) # to list
1515
else:
1516
instances = range(cmd_opts.instance, cmd_opts.instance + cmd_opts.count)
1517
1518
if cmd_opts.instance == 0:
1519
kill_tasks()
1520
1521
if cmd_opts.tracker:
1522
start_antenna_tracker(cmd_opts)
1523
1524
if cmd_opts.can_peripherals or frame_infos.get('periph_params_filename', None) is not None:
1525
start_CAN_Periph(cmd_opts, frame_infos)
1526
1527
if cmd_opts.custom_location:
1528
location = [(float)(x) for x in cmd_opts.custom_location.split(",")]
1529
progress("Starting up at %s" % (location,))
1530
elif cmd_opts.location is not None:
1531
location = find_location_by_name(cmd_opts.location)
1532
progress("Starting up at %s (%s)" % (location, cmd_opts.location))
1533
else:
1534
progress("Starting up at SITL location")
1535
location = None
1536
if cmd_opts.swarm is not None:
1537
offsets = find_offsets(instances, cmd_opts.swarm)
1538
elif cmd_opts.auto_offset_line is not None:
1539
if location is None:
1540
raise ValueError("location needed for auto-offset-line")
1541
(bearing, metres) = cmd_opts.auto_offset_line.split(",")
1542
bearing = float(bearing)
1543
metres = float(metres)
1544
dist = 0
1545
offsets = {}
1546
for x in instances:
1547
offsets[x] = [dist*math.sin(math.radians(bearing)), dist*math.cos(math.radians(bearing)), 0, 0]
1548
dist += metres
1549
else:
1550
offsets = {x: [0.0, 0.0, 0.0, None] for x in instances}
1551
if location is not None:
1552
spawns = find_spawns(location, offsets)
1553
else:
1554
spawns = None
1555
1556
if cmd_opts.use_dir is not None:
1557
base_dir = os.path.realpath(cmd_opts.use_dir)
1558
try:
1559
os.makedirs(base_dir)
1560
except OSError as exception:
1561
if exception.errno != errno.EEXIST:
1562
raise
1563
os.chdir(base_dir)
1564
else:
1565
base_dir = os.getcwd()
1566
instance_dir = []
1567
if len(instances) == 1:
1568
instance_dir.append(base_dir)
1569
else:
1570
for i in instances:
1571
i_dir = os.path.join(base_dir, str(i))
1572
try:
1573
os.makedirs(i_dir)
1574
except OSError as exception:
1575
if exception.errno != errno.EEXIST:
1576
raise
1577
finally:
1578
instance_dir.append(i_dir)
1579
1580
if True:
1581
if not cmd_opts.no_rebuild: # i.e. we should rebuild
1582
do_build(cmd_opts, frame_infos)
1583
1584
if cmd_opts.fresh_params:
1585
do_build_parameters(cmd_opts.vehicle)
1586
1587
if cmd_opts.vehicle_binary is not None:
1588
vehicle_binary = cmd_opts.vehicle_binary
1589
else:
1590
binary_basedir = "build/sitl"
1591
vehicle_binary = os.path.join(root_dir,
1592
binary_basedir,
1593
frame_infos["waf_target"])
1594
1595
if not os.path.exists(vehicle_binary):
1596
print("Vehicle binary (%s) does not exist" % (vehicle_binary,))
1597
sys.exit(1)
1598
1599
start_vehicle(vehicle_binary,
1600
cmd_opts,
1601
frame_infos,
1602
spawns=spawns)
1603
1604
1605
if cmd_opts.delay_start:
1606
progress("Sleeping for %f seconds" % (cmd_opts.delay_start,))
1607
time.sleep(float(cmd_opts.delay_start))
1608
1609
tmp = None
1610
if cmd_opts.frame in ['scrimmage-plane', 'scrimmage-copter']:
1611
# import only here so as to avoid jinja dependency in whole script
1612
from jinja2 import Environment, FileSystemLoader
1613
from tempfile import mkstemp
1614
entities = []
1615
config = {}
1616
config['plane'] = cmd_opts.vehicle == 'ArduPlane'
1617
if location is not None:
1618
config['lat'] = location[0]
1619
config['lon'] = location[1]
1620
config['alt'] = location[2]
1621
entities = {}
1622
for i in instances:
1623
(x, y, z, heading) = offsets[i]
1624
entities[i] = {
1625
'x': x, 'y': y, 'z': z, 'heading': heading,
1626
'to_ardupilot_port': 9003 + i * 10,
1627
'from_ardupilot_port': 9002 + i * 10,
1628
'to_ardupilot_ip': '127.0.0.1'
1629
}
1630
if cmd_opts.scrimmage_args is not None:
1631
scrimmage_args = cmd_opts.scrimmage_args.split(',')
1632
global_opts = ['terrain']
1633
instance_opts = ['motion_model', 'visual_model']
1634
for arg in scrimmage_args:
1635
arg = arg.split('=', 2)
1636
if len(arg) == 2:
1637
k, v = arg
1638
if k in global_opts:
1639
config[k] = v
1640
elif k in instance_opts:
1641
for i in entities:
1642
# explicit instance args take precedence; don't overwrite
1643
if k not in entities[i]:
1644
entities[i][k] = v
1645
elif len(arg) == 3:
1646
i, k, v = arg
1647
try:
1648
i = int(i)
1649
except ValueError:
1650
continue
1651
if i in entities and k in instance_opts:
1652
entities[i][k] = v
1653
config['entities'] = list(entities.values())
1654
env = Environment(loader=FileSystemLoader(os.path.join(autotest_dir, 'template')))
1655
mission = env.get_template('scrimmage.xml.j2').render(**config)
1656
tmp = mkstemp()
1657
atexit.register(os.remove, tmp[1])
1658
1659
with os.fdopen(tmp[0], 'w') as fd:
1660
fd.write(mission)
1661
run_in_terminal_window('SCRIMMAGE', ['scrimmage', tmp[1]])
1662
1663
1664
if cmd_opts.delay_start:
1665
progress("Sleeping for %f seconds" % (cmd_opts.delay_start,))
1666
time.sleep(float(cmd_opts.delay_start))
1667
1668
try:
1669
if cmd_opts.no_mavproxy:
1670
time.sleep(3) # output our message after run_in_terminal_window.sh's
1671
progress("Waiting for SITL to exit")
1672
wait_unlimited()
1673
else:
1674
start_mavproxy(cmd_opts, frame_infos)
1675
except KeyboardInterrupt:
1676
progress("Keyboard Interrupt received ...")
1677
1678
sys.exit(0)
1679
1680