#!/usr/bin/env python3 import sys, os, re, math def near(a, b): fudge_factor = 0.001 error = abs(a-b) allowance = abs(a * fudge_factor) if error <= allowance: return True return False def must_decel_to_avoid_pos_violation(sample, out, vel): """Returns True if maximum deceleration is needed NOW to avoid violating position constraints.""" # Keep going one cycle without any acceleration. next_out = out + (vel * 0.001) # Assume a 1 ms period. # v = v0 + at # 0 = v0 + at # t = v0/a seconds_to_stop = abs(vel) / params['maxa'] # figure out which direction we need to accelerate in if vel > 0: accel_sign = -1 else: accel_sign = 1 # r = r0 + v0t + 1/2 at^2 pos_at_stop = next_out + (vel * seconds_to_stop) + (accel_sign * 0.5 * params['maxa'] * math.pow(seconds_to_stop, 2)) print("sample=%d, next_out=%.6f, vel=%.6f, seconds_to_stop=%.6f, pos_at_stop=%.6f, min=%.6f, max=%.6f" % \ (sample, next_out, vel, seconds_to_stop, pos_at_stop, params['min'], params['max'])) if pos_at_stop > (params['max'] * 1.01) or pos_at_stop < (params['min'] * 1.01): return True return False halfile = os.path.join(os.path.dirname(__file__),"test.hal") param_re = re.compile(r'^setp\s+limit3.0.([^\s]+)\s+([-0-9]+)$') params = {} params['min'] = -1e20 params['max'] = 1e20 params['maxv'] = 1e20 params['maxa'] = 1e20 with open(halfile, 'r') as f: for line in f: m = param_re.match(line) if m is None: continue params[m.group(1)] = float(m.group(2)) result_filename = sys.argv[1] result_file = open(result_filename, 'r') retval = 0 for line in result_file.readlines(): (sample, in_val, out, vel, acc) = line.split()[:5] sample = int(sample) in_val = float(in_val) out = float(out) vel = float(vel) acc = float(acc) # # See if we've violated any constraints. # if float(out) > params['max']: print("max=%.3f, in=%.3f, out=%.3f, max violation on sample %s" % \ (params['max'], float(in_val), out, sample)) retval = 1 if float(out) < params['min']: print("min=%.3f, in=%.3f, out=%.3f, min violation on sample %s" % \ (params['min'], float(in_val), out, sample)) retval = 1 if abs(vel) > params['maxv']: print("maxv=%.3f, vel=%.3f, velocity violation on sample %s" % \ (params['maxv'], vel, sample)) retval = 1 if abs(acc) > params['maxa']: print("maxa=%.3f, acc=%.3f, acceleration violation on sample %s" % \ (params['maxa'], acc, sample)) retval = 1 # # See if we're accelerating too sluggishly. # if abs(acc) < params['maxa']: # Why aren't we accelerating at the max rate? if abs(vel) == params['maxv']: # We've already maxed out vel. pass elif near(out, params['min']) or near(out, params['max']): # We're at our position limit. pass elif near(out, in_val): # We're already near our target. pass else: # We have no good reason for not accelerating hard, so fail this test. print("maxa=%.3f, acc=%.3f, sluggish acceleration on sample %s" % \ (params['maxa'], acc, sample)) retval = 1 # # See if our velocity is too sluggish. # if abs(vel) < params['maxv']: # Why aren't we moving at the max rate? if abs(acc) == params['maxa']: # We're still accelerating. pass elif near(out, params['min']) or near(out, params['max']): # We're at our position limit. pass elif near(out, in_val): # We've already reached our target. pass elif must_decel_to_avoid_pos_violation(sample, out, vel): # We're slowing down to avoid violating position constraints. pass else: # We have no good reason for not going fast, so fail this test. print("maxv=%.3f, vel=%.3f, sluggish velocity on sample %s" % \ (params['maxv'], vel, sample)) retval = 1 sys.exit(retval)