#!/usr/bin/env python3 import subprocess import time import struct import random import math import statistics import tempfile import itertools from collections import defaultdict from os import path import tqdm SIMS = [ 'dec_proto_am_ber_top.py', 'dec_proto_am_dc_ber_top.py', 'dec_proto_fm_ber_top.py', ] E12_SERIES = [1.0, 1.2, 1.5, 1.8, 2.2, 2.7, 3.3, 3.9, 4.7, 5.6, 6.8, 8.2] AMPLITUDES_MILLIHERTZ = [ x*y for y in [1, 10, 100] for x in E12_SERIES ] SIMULATION_DURATION = 30.0 # seconds realtime MAX_CONCURRENT_PROCESSES = 8 def run_simulation( duration = SIMULATION_DURATION, simulations = SIMS, forklimit = MAX_CONCURRENT_PROCESSES, amplitudes:'list(millihertz)' = AMPLITUDES_MILLIHERTZ, terminate_timeout:'s' = 5.0, communicate_timeout:'s' = 10.0, repeat_runs = 1, shuffle = False, tqdm = tqdm.tqdm ): with tempfile.TemporaryDirectory() as tmpdir: jobs = list(enumerate(itertools.product(simulations, amplitudes * repeat_runs))) if shuffle: random.shuffle(jobs) nchunks = int(math.ceil(len(jobs)/forklimit)) def start_processes(jobs): for i, (sim, ampl_mhz) in jobs: berfile = path.join(tmpdir, f'berfile_{i}') proc = subprocess.Popen(['/usr/bin/python2', sim, '--signal-strength', str(ampl_mhz), '--ber-file', berfile], stdin=subprocess.PIPE, stdout=subprocess.DEVNULL) yield proc, sim, ampl_mhz, berfile results = { sim: defaultdict(lambda: ([], [])) for sim in simulations } with tqdm(total = nchunks * duration, bar_format='{l_bar}{bar}| {elapsed}<{remaining}') as tq: tq.write(f'Will launch {len(jobs)} simulation jobs in {nchunks} batches of {forklimit}') for n, i in enumerate(range(0, len(jobs), forklimit)): batch = jobs[i:][:forklimit] tq.write(f'Starting batch {n+1}/{nchunks}...') processes = list(start_processes(batch)) tq.write('done.') tq.write('Waiting for simulation:') for _ in range(100): time.sleep(duration/100) tq.update(duration/100) tq.write('Terminating processes...') for proc, *_ in processes: proc.communicate(b'\n', timeout=communicate_timeout) for proc, *_ in processes: proc.wait(terminate_timeout) tq.write('done.') tq.write('Processing simulation results') for _proc, sim, ampl_mhz, berfile in processes: with open(berfile, 'rb') as f: data = f.read() floats = struct.unpack(f'{len(data)//4}f', data) ber = statistics.mean(floats[-256:]) stdev = statistics.stdev(floats[-256:]) bers, stdevs = results[sim][ampl_mhz] bers.append(ber) stdevs.append(stdev) return results if __name__ == '__main__': print(run_simulation())