aboutsummaryrefslogtreecommitdiffstats
path: root/host/utils/latency/run_tests.py
diff options
context:
space:
mode:
Diffstat (limited to 'host/utils/latency/run_tests.py')
-rwxr-xr-xhost/utils/latency/run_tests.py222
1 files changed, 222 insertions, 0 deletions
diff --git a/host/utils/latency/run_tests.py b/host/utils/latency/run_tests.py
new file mode 100755
index 000000000..f0cb31ffb
--- /dev/null
+++ b/host/utils/latency/run_tests.py
@@ -0,0 +1,222 @@
+#!/usr/bin/env python
+#
+# Copyright 2012 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+import subprocess, time
+from optparse import OptionParser
+from string import split
+import sys
+import os
+
+from gnuradio.eng_option import eng_option
+
+
+def launch_test(args="", rate=None, spb=None, spp=0, prefix="", suffix="", extra=[], verbose=False, title=None):
+ real = os.path.realpath(__file__)
+ basedir = os.path.dirname(real)
+ responder = [
+ os.path.join(basedir, "responder")
+ ]
+
+ if args is not None and len(args) > 0:
+ responder += ["--args=" + args]
+ if rate is not None and rate > 0:
+ responder += ["--rate=%f" % (rate)]
+ if spb is not None and spb > 0:
+ responder += ["--spb=%d" % (spb)]
+ if spp is not None and spp > 0:
+ responder += ["--spp=%d" % (spp)]
+ if prefix is not None and len(prefix) > 0:
+ responder += ["--stats-file-prefix=" + prefix]
+ if suffix is not None and len(suffix) > 0:
+ responder += ["--stats-file-suffix=" + suffix]
+ if extra is not None:
+ responder += extra
+ if title is not None and len(title) > 0:
+ responder += ["--title=\"" + title + "\""]
+ if verbose:
+ print "==> Executing:", " ".join(responder)
+ try:
+ responder += ["--log-file"] # This will produce another output file with logs
+ responder += ["--combine-eob"]
+ p = subprocess.Popen(responder)
+ res = p.wait() # make sure subprocess finishes
+ except KeyboardInterrupt:
+ res = p.wait() # even in CTRL+C case wait till subprocess finishes
+ print "==> Caught CTRL+C"
+ return None
+
+ return res
+
+# These return codes should match the C++ return codes
+class ReturnCode:
+ RETCODE_OK = 0
+ RETCODE_BAD_ARGS = -1
+ RETCODE_RUNTIME_ERROR = -2
+ RETCODE_UNKNOWN_EXCEPTION = -3
+ RETCODE_RECEIVE_TIMEOUT = -4
+ RETCODE_RECEIVE_FAILED = -5
+ RETCODE_MANUAL_ABORT = -6
+ RETCODE_BAD_PACKET = -7
+ RETCODE_OVERFLOW = -8
+
+
+def get_initialized_OptionParser():
+ def_rates = ".25 1 4 8 25"
+ usage = "%prog: [options] -- [extra arguments]"
+ parser = OptionParser(option_class=eng_option, usage=usage)
+
+ parser.add_option("", "--rates", type="string", help="sample rates (Msps) [default: %default]", default=def_rates)
+ parser.add_option("", "--spbs", type="string", help="samples per block [default: %default]",
+ default="32 64 256 1024")
+ parser.add_option("", "--spps", type="string", help="samples per packet (0: driver default) [default: %default]",
+ default="0 64 128 256 512")
+ parser.add_option("", "--args", type="string", help="UHD device arguments [default: %default]", default=None)
+ parser.add_option("", "--prefix", type="string", help="Stats filename prefix [default: %default]", default=None)
+ parser.add_option("", "--suffix", type="string", help="Stats filename suffix [default: %default]", default=None)
+ parser.add_option("", "--pause", action="store_true", help="pause between tests [default=%default]", default=False)
+ parser.add_option("", "--interactive", action="store_true", help="enable prompts within test [default=%default]",
+ default=False)
+ parser.add_option("", "--wait", type="float", help="time to wait between tests (seconds) [default=%default]",
+ default=0.0)
+ parser.add_option("", "--abort", action="store_true", help="abort on error [default=%default]", default=False)
+ parser.add_option("", "--verbose", action="store_true", help="be verbose [default=%default]", default=False)
+ parser.add_option("", "--title", type="string", help="test title [default: %default]", default=None)
+
+ return parser
+
+
+def set_gen_prefix(prefix, save_dir):
+ if not save_dir[-1] == "/":
+ save_dir = save_dir + "/"
+
+ if prefix == None:
+ if os.path.exists(save_dir) is not True:
+ os.makedirs(save_dir)
+ prefix = save_dir
+ return prefix
+
+
+def get_extra_args(options, args):
+ extra_args = {
+ "adjust-simulation-rate": None,
+ "time-mul": "1e6",
+ "test-success": 5,
+ "simulate": 1000,
+ "iterations": 1000,
+ "delay-min": "50e-6",
+ "delay-max": "5e-3",
+ "delay-step": "50e-6",
+ }
+
+ if options.interactive is not True:
+ extra_args["batch-mode"] = None
+ if options.pause is True:
+ extra_args["pause"] = None
+
+ for arg in args:
+ if len(arg) > 2 and arg[0:2] == "--":
+ arg = arg[2:]
+ idx = arg.find('=')
+ if idx == -1:
+ extra_args[arg] = None
+ else:
+ extra_args[arg[0:idx]] = arg[idx + 1:]
+
+ def _format_arg(d, k):
+ a = "--" + str(k)
+ if d[k] is not None:
+ a += "=" + str(d[k])
+ return a
+
+ extra = map(lambda x: _format_arg(extra_args, x), extra_args)
+
+ print "\n".join(map(lambda x: str(x) + " = " + str(extra_args[x]), extra_args.keys()))
+
+ return extra
+
+
+def wait_for_keyboard():
+ try:
+ print "\nPress ENTER to start..."
+ raw_input()
+ return ReturnCode.RETCODE_OK
+ except KeyboardInterrupt:
+ print "Aborted"
+ return ReturnCode.RETCODE_MANUAL_ABORT
+
+
+def main():
+ parser = get_initialized_OptionParser()
+ (options, args) = parser.parse_args()
+
+ save_dir = "results"
+ options.prefix = set_gen_prefix(options.prefix, save_dir)
+ extra = get_extra_args(options, args)
+
+ rates = map(lambda x: float(x) * 1e6, split(options.rates))
+ spbs = map(int, split(options.spbs))
+ spps = map(int, split(options.spps))
+ total = len(rates) * len(spbs) * len(spps)
+
+ title = options.title or ""
+ if len(title) >= 2 and title[0] == "\"" and title[-1] == "\"":
+ title = title[1:-1]
+
+ count = 0
+ results = {}
+
+ try:
+ for rate in rates:
+ results_rates = results[rate] = {}
+ for spb in spbs:
+ results_spbs = results_rates[spb] = {}
+ for spp in spps:
+ if count > 0:
+ if options.pause:
+ print "Press ENTER to begin next test..."
+ raw_input()
+ elif options.wait > 0:
+ time.sleep(options.wait)
+ title = "Test #%d of %d (%d%% complete, %d to go)" % (
+ count + 1, total, int(100 * count / total), total - count - 1)
+ res = launch_test(options.args, rate, spb, spp, options.prefix, options.suffix, extra,
+ options.verbose, title)
+ sys.stdout.flush()
+ count += 1
+ # Break out of loop. Exception thrown if Ctrl + C was pressed.
+ if res is None:
+ raise Exception
+ results_spbs[spp] = res
+ if res < 0 and (res == ReturnCode.RETCODE_MANUAL_ABORT or options.abort):
+ raise Exception
+ except:
+ pass
+
+ for rate in results.keys():
+ results_rates = results[rate]
+ for spb in results_rates.keys():
+ results_spbs = results_rates[spb]
+ for spp in results_spbs.keys():
+ res = results_spbs[spp]
+ print res, ":", rate, spb, spp
+ print "Tests finished"
+ return 0
+
+
+if __name__ == '__main__':
+ main()