def setUpClass(cls, flash=False): cls.host = Host() if flash: cls.host.build() else: print("Resetting the machine") cls.host.reset()
def setUpClass(cls, flash=False, cam=True): cls.host = Host() cls.host.enable_steppers = False cls.cam = cam if flash: cls.host.build() else: print('Resetting the machine') cls.host.reset() if cam: cls.cam = camera.Cam() cls.cam.init()
class TestDispatcher(SPIGatewareTestCase): platform = TestPlatform() FRAGMENT_UNDER_TEST = Dispatcher FRAGMENT_ARGUMENTS = {'platform': platform, 'divider': 1, 'simdiode': True} def initialize_signals(self): self.host = Host(self.platform) self.host.spi_exchange_data = self.spi_exchange_data yield self.dut.spi.cs.eq(0) def wait_complete(self): '''helper method to wait for completion''' cntr = 0 while (yield self.dut.busy) or (cntr < 100): if (yield self.dut.pol.busy): cntr = 0 else: cntr += 1 yield @sync_test_case def test_memfull(self): '''write move instruction until memory is full, execute and ensure there is no parser error i.e. error ''' platform = self.platform # should fill the memory as move instruction is # larger than the memdepth self.assertEqual((yield from self.host.memfull()), False) yield from self.host._executionsetter(False) try: for _ in range(platform.memdepth): yield from self.host.send_move([1000], [1]*platform.motors, [2]*platform.motors, [3]*platform.motors, maxtrials=1) except Memfull: pass self.assertEqual((yield from self.host.memfull()), True) yield from self.host._executionsetter(True) # data should now be processed from sram and empty become 1 while (yield self.dut.parser.empty) == 0: yield # 2 clocks needed for error to propagate yield yield self.assertEqual((yield from self.host.error), False) @sync_test_case def test_readdiode(self): '''verify you can receive photodiode trigger Photodiode trigger simply checks wether the photodiode has been triggered for each cycle. The photodiode is triggered by the simdiode. ''' yield from self.host._executionsetter(False) yield from self.host.enable_comp(laser0=True, polygon=True) for _ in range(self.dut.laserhead.dct['TICKSINFACET']*2): yield self.assertEqual((yield self.dut.laserhead.photodiode_t), False) # not triggered as laser and polygon not on yield from self.host._executionsetter(True) val = (yield from self.host.pinstate)['photodiode_trigger'] for _ in range(self.dut.laserhead.dct['TICKSINFACET']*2): yield self.assertEqual((yield self.dut.laserhead.photodiode_t), True) self.assertEqual(val, True) @sync_test_case def test_writepin(self): '''verify homing procedure works correctly''' yield from self.host.enable_comp(laser0=True, laser1=False, polygon=False) # wait till instruction is received while (yield self.dut.parser.empty): yield yield self.assertEqual((yield from self.host.error), False) self.assertEqual((yield self.dut.laserheadpins.laser0), 1) self.assertEqual((yield self.dut.laserheadpins.laser1), 0) self.assertEqual((yield self.dut.laserheadpins.en), 0) @sync_test_case def test_home(self): '''verify homing procedure works correctly''' self.host._position = np.array([0.1]*self.platform.motors) for i in range(self.platform.motors): yield self.dut.steppers[i].limit.eq(1) yield self.assertEqual((yield self.dut.parser.pinstate[0]), 1) yield from self.host.home_axes(axes=np.array([1]*self.platform.motors), speed=None, pos=-0.1) assert_array_equal(self.host._position, np.array([0]*self.platform.motors)) @sync_test_case def test_invalidwrite(self): '''write invalid instruction and verify error is raised''' fifo = self.dut.parser.fifo self.assertEqual((yield from self.host.error), False) # write illegal byte to queue and commit yield fifo.write_data.eq(0xAA) yield from self.pulse(fifo.write_en) yield from self.pulse(fifo.write_commit) self.assertEqual((yield self.dut.parser.empty), 0) # data should now be processed from sram and empty become 1 while (yield self.dut.parser.empty) == 0: yield # 2 clocks needed for error to propagate yield yield self.assertEqual((yield from self.host.error), True) @sync_test_case def test_ptpmove(self, steps=[800], ticks=[30_000]): '''verify point to point move If ticks is longer than tick limit the moves is broken up. If the number of instruction is larger than memdepth it also test blocking behaviour. ''' steps = steps*self.platform.motors mm = np.array(steps)/np.array(list(self.platform.stepspermm.values())) time = np.array(ticks)/FREQ speed = mm/time yield from self.host.gotopoint(mm.tolist(), speed.tolist()) yield from self.wait_complete() calculated = deepcopy(self.host._position) assert calculated.sum() > 0 assert_array_equal((yield from self.host.position), calculated) @sync_test_case def test_movereceipt(self, ticks=10_000): 'verify move instruction send over with send_move' a = list(range(1, self.platform.motors+1)) b = list(range(3, self.platform.motors+3)) c = list(range(5, self.platform.motors+5)) yield from self.host.send_move([ticks], a, b, c) # wait till instruction is received while (yield self.dut.pol.start) == 0: yield yield while (yield self.dut.pol.busy): yield # confirm receipt tick limit and coefficients self.assertEqual((yield self.dut.pol.ticklimit), 10_000) coefficients = [a, b, c] for motor in range(self.platform.motors): for coef in range(DEGREE): indx = motor*(DEGREE)+coef self.assertEqual((yield self.dut.pol.coeff[indx]), coefficients[coef][motor]) while (yield self.dut.pol.busy): yield for motor in range(self.platform.motors): self.assertEqual((yield self.dut.pol.cntrs[motor*DEGREE]), a[motor]*ticks + b[motor]*pow(ticks, 2) + c[motor]*pow(ticks, 3)) @sync_test_case def test_writeline(self, numblines=3): 'write line and see it is processed accordingly' host = self.host for _ in range(numblines): yield from self.host.writeline([1] * host.laser_params['BITSINSCANLINE']) yield from host.writeline([]) self.assertEqual((yield from self.host.pinstate)['synchronized'], True) yield from self.host.enable_comp(synchronize=False) while (yield self.dut.parser.empty) == 0: yield self.assertEqual((yield from self.host.pinstate)['synchronized'], False) self.assertEqual((yield from self.host.error), False)
def initialize_signals(self): self.host = Host(self.platform) self.host.spi_exchange_data = self.spi_exchange_data yield self.dut.spi.cs.eq(0)
class TestParser(SPIGatewareTestCase): platform = TestPlatform() FRAGMENT_UNDER_TEST = SPIParser FRAGMENT_ARGUMENTS = {'platform': platform} def initialize_signals(self): self.host = Host(self.platform) self.host.spi_exchange_data = self.spi_exchange_data yield self.dut.spi.cs.eq(0) def instruction_ready(self, check): while (yield self.dut.empty) == 1: yield # Instruction ready self.assertEqual((yield self.dut.empty), 0) self.assertEqual((yield self.dut.fifo.space_available), (self.platform.memdepth - check)) @sync_test_case def test_getposition(self): decimals = 3 position = [randint(-2000, 2000) for _ in range(self.platform.motors)] for idx, pos in enumerate(self.dut.position): yield pos.eq(position[idx]) lst = (yield from self.host.position).round(decimals) stepspermm = np.array(list(self.platform.stepspermm.values())) assert_array_equal(lst, (position/stepspermm).round(decimals)) @sync_test_case def test_writescanline(self): host = self.host yield from host.writeline([1] * host.laser_params['BITSINSCANLINE']) while (yield self.dut.empty) == 1: yield wordslaser = wordsinscanline(params(self.platform)['BITSINSCANLINE']) yield from self.instruction_ready(wordslaser) @sync_test_case def test_lastscanline(self): yield from self.host.writeline([]) yield from self.instruction_ready(1) @sync_test_case def test_writepin(self): 'write move instruction and verify FIFO is no longer empty' self.assertEqual((yield self.dut.empty), 1) yield from self.host.enable_comp(laser0=True, laser1=False, polygon=False) yield from self.instruction_ready(1) @sync_test_case def test_writemoveinstruction(self): 'write move instruction and verify FIFO is no longer empty' self.assertEqual((yield self.dut.empty), 1) yield from self.host.send_move([1000], [1]*self.platform.motors, [2]*self.platform.motors, [3]*self.platform.motors) words = wordsinmove(self.platform.motors) yield from self.instruction_ready(words) @sync_test_case def test_readpinstate(self): '''set pins to random state''' def test_pins(): olddct = (yield from self.host.pinstate) for k in olddct.keys(): olddct[k] = randint(0, 1) bitlist = list(olddct.values()) bitlist.reverse() b = int("".join(str(i) for i in bitlist), 2) yield self.dut.pinstate.eq(b) yield newdct = (yield from self.host.pinstate) self.assertDictEqual(olddct, newdct) yield from test_pins() yield from test_pins() @sync_test_case def test_enableparser(self): '''enables SRAM parser via command and verifies status with different command''' yield from self.host._executionsetter(False) self.assertEqual((yield self.dut.execute), 0) self.assertEqual((yield from self.host.execution), 0) @sync_test_case def test_invalidwrite(self): '''write invalid instruction and verify error is raised''' command = [COMMANDS.WRITE] + [0]*WORD_BYTES yield from self.host.send_command(command) self.assertEqual((yield from self.host.error), True) @sync_test_case def test_memfull(self): 'write move instruction until memory is full' platform = self.platform self.assertEqual((yield self.dut.empty), 1) # should fill the memory as move instruction is # larger than the memdepth self.assertEqual((yield from self.host.memfull()), False) try: for _ in range(platform.memdepth): yield from self.host.send_move([1000], [1]*platform.motors, [2]*platform.motors, [3]*platform.motors, maxtrials=1) except Memfull: pass self.assertEqual((yield from self.host.memfull()), True)
def initialize_signals(self): self.host = Host(self.platform) yield self.dut.ticklimit.eq(MOVE_TICKS)
class TestPolynomal(LunaGatewareTestCase): platform = TestPlatform() FRAGMENT_UNDER_TEST = Polynomal FRAGMENT_ARGUMENTS = {'platform': platform} def initialize_signals(self): self.host = Host(self.platform) yield self.dut.ticklimit.eq(MOVE_TICKS) def count_steps(self, motor): '''counts steps in accounting for direction''' count = 0 while (yield self.dut.busy): old = (yield self.dut.step[motor]) yield if old and ((yield self.dut.step[motor]) == 0): if (yield self.dut.dir[motor]): count += 1 else: count -= 1 return count def send_coefficients(self, a, b, c): '''send coefficients and pulse start a,b,c -- for cx^3+bx^2+ax ''' coefs = [a, b, c] # load coefficients for motor in range(self.platform.motors): for coef in range(self.dut.order): yield self.dut.coeff[coef].eq(coefs[coef]) yield from self.pulse(self.dut.start) @sync_test_case def test_ticklimit(self): ''' Test wich max speed can be reached Suppose max RPM stepper motor is 600, microstepping 16, update frequency 1 MHz --> (600*60*16)/1E6 = 0.576 step per tick. If there are 10_000 ticks per segment; 5760 steps is maximum you can do in one segment from a physical point of view At max speed, the motor is on and subsequenctly off in subsequent steps, ergo your motor update frequency determines max speed (see also Nyquist frequency) ''' def limittest(limit, steps): a = round(self.host.steps_to_count(steps) / limit) yield self.dut.ticklimit.eq(limit) yield from self.send_coefficients(a, 0, 0) step_count = (yield from self.count_steps(0)) self.assertEqual(step_count, steps) yield from limittest(MOVE_TICKS, 4000) yield from limittest(10_000, 1) @sync_test_case def test_calculation(self, a=2, b=3, c=1): ''' Test a simple relation e.g. cx^3+bx^2+ax ''' if self.dut.order < 3: c = 0 if self.dut.order < 2: b = 0 yield from self.send_coefficients(a, b, c) while (yield self.dut.busy): yield self.assertEqual( (yield self.dut.cntrs[0]), a * MOVE_TICKS + b * pow(MOVE_TICKS, 2) + c * pow(MOVE_TICKS, 3)) @sync_test_case def test_jerk(self): '''Test lower limit of highest degree, e.g. order 3 the jerk Smallest value required is defined by pure higest order move with 1 step. Test if higest order move can be executed with one step. ''' steps = 1 coef = round( self.host.steps_to_count(steps) / pow(MOVE_TICKS, self.dut.order)) coeffs = [0] * 3 coeffs[self.dut.order - 1] = coef yield from self.send_coefficients(*coeffs) step_count = (yield from self.count_steps(0)) self.assertEqual(step_count, steps) @sync_test_case def test_move(self): '''Movement Test forward and backward move at constant speed. The largest constant in polynomal is determined by pure velocity move with half time limit as steps. ''' def do_move(steps): # NOTE: (a = s/t) != -1*(-s/t) # might be due to rounding and bitshift a = round(self.host.steps_to_count(steps) / MOVE_TICKS) yield from self.send_coefficients(a, 0, 0) count = (yield from self.count_steps(0)) self.assertEqual(count, steps) steps = round(0.4 * MOVE_TICKS) yield from do_move(steps) yield from do_move(-steps)
class TestDispatcher(SPIGatewareTestCase): platform = TestPlatform() FRAGMENT_UNDER_TEST = Dispatcher FRAGMENT_ARGUMENTS = {'platform': platform, 'simdiode': True} def initialize_signals(self): self.host = Host(self.platform) self.host.spi_exchange_data = self.spi_exchange_data yield self.dut.spi.cs.eq(0) def wait_complete(self): '''helper method to wait for completion''' cntr = 0 while (yield self.dut.busy) or (cntr < 100): if (yield self.dut.pol.busy): cntr = 0 else: cntr += 1 yield @sync_test_case def test_memfull(self): '''write move instruction until memory is full, enable parser and ensure there is no parser error. ''' platform = self.platform # should fill the memory as move instruction is # larger than the memdepth self.assertEqual((yield from self.host.get_state())['mem_full'], False) yield from self.host.set_parsing(False) try: for _ in range(platform.memdepth): yield from self.host.spline_move(1000, [1] * platform.motors) except Memfull: pass self.assertEqual((yield from self.host.get_state())['mem_full'], True) yield from self.host.set_parsing(True) # data should now be processed from sram and empty become 1 while (yield self.dut.parser.empty) == 0: yield # 2 clocks needed for error to propagate yield yield self.assertEqual((yield from self.host.get_state())['error'], False) @sync_test_case def test_readdiode(self): '''verify you can receive photodiode trigger Photodiode trigger simply checks wether the photodiode has been triggered for each cycle. The photodiode is triggered by the simdiode. ''' yield from self.host.set_parsing(False) yield from self.host.enable_comp(laser0=True, polygon=True) for _ in range(self.dut.laserhead.dct['TICKSINFACET'] * 2): yield self.assertEqual((yield self.dut.laserhead.photodiode_t), False) # not triggered as laser and polygon not on yield from self.host.set_parsing(True) val = (yield from self.host.get_state())['photodiode_trigger'] for _ in range(self.dut.laserhead.dct['TICKSINFACET'] * 2): yield self.assertEqual((yield self.dut.laserhead.photodiode_t), True) self.assertEqual(val, True) @sync_test_case def test_writepin(self): '''verify homing procedure works correctly''' yield from self.host.enable_comp(laser0=True, laser1=False, polygon=False) # wait till instruction is received while (yield self.dut.parser.empty): yield yield self.assertEqual((yield from self.host.get_state())['error'], False) self.assertEqual((yield self.dut.laserheadpins.laser0), 1) self.assertEqual((yield self.dut.laserheadpins.laser1), 0) self.assertEqual((yield self.dut.laserheadpins.en), 0) @sync_test_case def test_home(self): '''verify homing procedure works correctly''' self.host._position = np.array([0.1] * self.platform.motors) for i in range(self.platform.motors): yield self.dut.steppers[i].limit.eq(1) yield self.assertEqual((yield self.dut.parser.pinstate[0]), 1) yield from self.host.home_axes(axes=np.array([1] * self.platform.motors), speed=None, displacement=-0.1) assert_array_equal(self.host._position, np.array([0] * self.platform.motors)) @sync_test_case def test_invalidwrite(self): '''write invalid instruction and verify error is raised''' fifo = self.dut.parser.fifo self.assertEqual((yield from self.host.get_state())['error'], False) # write illegal byte to queue and commit yield fifo.write_data.eq(0xAA) yield from self.pulse(fifo.write_en) yield from self.pulse(fifo.write_commit) self.assertEqual((yield self.dut.parser.empty), 0) # data should now be processed from sram and empty become 1 while (yield self.dut.parser.empty) == 0: yield # 2 clocks needed for error to propagate yield yield self.assertEqual((yield from self.host.get_state())['error'], True) @sync_test_case def test_ptpmove(self, steps=[800], ticks=30_000): '''verify point to point move If ticks is longer than tick limit the moves is broken up. If the number of instruction is larger than memdepth it also test blocking behaviour. ''' steps = steps * self.platform.motors mm = -np.array(steps) / np.array( list(self.platform.stepspermm.values())) time = ticks / MOTORFREQ speed = np.absolute(mm / time) yield from self.host.gotopoint(mm.tolist(), speed.tolist()) yield from self.wait_complete() # if 76.3 steps per mm then 1/76.3 = 0.013 is max resolution assert_array_almost_equal((yield from self.host.position), mm, decimal=1) # TODO: they are not symmetric! if start with mm does not work mm = -mm yield from self.host.gotopoint(mm.tolist(), speed.tolist(), absolute=False) yield from self.wait_complete() assert_array_almost_equal((yield from self.host.position), np.zeros(self.platform.motors), decimal=1) @sync_test_case def test_movereceipt(self, ticks=10_000): 'verify move instruction send over with spline move' platform = self.platform coeff = [randint(-10, 10)] * platform.motors * platform.poldegree yield from self.host.spline_move(ticks, coeff) # wait till instruction is received while (yield self.dut.pol.start) == 0: yield yield while (yield self.dut.pol.busy): yield # confirm receipt tick limit and coefficients self.assertEqual((yield self.dut.pol.ticklimit), ticks) for motor in range(platform.motors): for coef in range(platform.poldegree): indx = motor * platform.poldegree + coef self.assertEqual((yield self.dut.pol.coeff[indx]), coeff[indx]) while (yield self.dut.pol.busy): yield for motor in range(platform.motors): cnt = 0 for degree in range(platform.poldegree): indx = motor * platform.poldegree + degree cnt += ticks**(degree + 1) * coeff[indx] self.assertEqual( (yield self.dut.pol.cntrs[motor * platform.poldegree]), cnt) @sync_test_case def test_writeline(self, numblines=20, stepsperline=0.5): 'write line and see it is processed accordingly' host = self.host for _ in range(numblines): yield from host.writeline( [1] * host.laser_params['BITSINSCANLINE'], stepsperline, 0) yield from host.writeline([]) self.assertEqual((yield from host.get_state())['synchronized'], True) while (yield self.dut.parser.empty) == 0: yield plat = host.platform stepspermm = plat.stepspermm[plat.laser_axis] decimals = np.log(stepspermm) // np.log(10) dist = numblines * stepsperline / stepspermm idx = list(plat.stepspermm.keys()).index(plat.laser_axis) # TODO: the x position changes as well!? assert_array_almost_equal(-dist, (yield from host.position)[idx], decimal=decimals) for _ in range(numblines): yield from host.writeline( [1] * host.laser_params['BITSINSCANLINE'], stepsperline, 1) yield from host.writeline([]) yield from host.enable_comp(synchronize=False) while (yield self.dut.parser.empty) == 0: yield # TODO: the engine should return to same position assert_array_almost_equal(0, (yield from host.position)[idx], decimal=decimals) self.assertEqual((yield from host.get_state())['synchronized'], False) self.assertEqual((yield from host.get_state())['error'], False)
class TestPolynomal(LunaGatewareTestCase): platform = TestPlatform() FRAGMENT_UNDER_TEST = Polynomal FRAGMENT_ARGUMENTS = {'platform': platform, 'divider': 1} def initialize_signals(self): self.host = Host(self.platform) yield self.dut.ticklimit.eq(MOVE_TICKS) def count_steps(self, motor): '''counts steps in accounting for direction''' count = 0 while (yield self.dut.busy): old = (yield self.dut.step[motor]) yield if old and ((yield self.dut.step[motor]) == 0): if (yield self.dut.dir[motor]): count += 1 else: count -= 1 return count def send_coefficients(self, a, b, c): '''send coefficients and pulse start a,b,c -- for cx^3+bx^2+ax ''' coefs = [a, b, c] # load coefficients for motor in range(self.platform.motors): for coef in range(self.dut.order): yield self.dut.coeff[coef].eq(coefs[coef]) yield from self.pulse(self.dut.start) @sync_test_case def test_ticklimit(self): ''' Test different upper tick limits''' def limittest(limit, steps): a = round(self.host.steps_to_count(steps) / limit) yield self.dut.ticklimit.eq(limit) yield from self.send_coefficients(a, 0, 0) while (yield self.dut.busy): yield self.assertEqual((yield self.dut.totalsteps[0]), steps) yield from limittest(5000, 10) yield from limittest(2000, 30) @sync_test_case def test_calculation(self, a=2, b=3, c=1): ''' Test a simple relation e.g. cx^3+bx^2+ax ''' yield from self.send_coefficients(a, b, c) while (yield self.dut.busy): yield self.assertEqual( (yield self.dut.cntrs[0]), a * MOVE_TICKS + b * pow(MOVE_TICKS, 2) + c * pow(MOVE_TICKS, 3)) @sync_test_case def test_jerk(self): '''Test lower limit of c, i.e. the jerk Smallest value required is defined by pure jerk move with 1 step. Test if jerk move can be executed with one step. ''' steps = 1 c = round(self.host.steps_to_count(steps) / pow(MOVE_TICKS, 3)) yield from self.send_coefficients(0, 0, c) while (yield self.dut.busy): yield dut_count = (yield self.dut.cntrs[0]) self.assertEqual(dut_count >> BIT_SHIFT, steps * 2) self.assertEqual((yield self.dut.totalsteps[0]), steps) @sync_test_case def test_move(self): '''Movement Test forward and backward move at constant speed. The largest constant in polynomal is determined by pure velocity move with half time limit as steps. ''' def do_move(steps): # NOTE: (a = s/t) != -1*(-s/t) # might be due to rounding and bitshift a = round(self.host.steps_to_count(steps) / MOVE_TICKS) yield from self.send_coefficients(a, 0, 0) count = (yield from self.count_steps(0)) self.assertEqual(count, steps) dut_count = (yield self.dut.cntrs[0]) self.assertEqual(dut_count >> BIT_SHIFT, steps * 2) self.assertEqual((yield self.dut.totalsteps[0]), steps) steps = round(0.4 * MOVE_TICKS) yield from do_move(steps) yield from do_move(-steps)