def test_tilts(): import nidaqmx # pylint: disable=import-error # pylint: disable=import-error from nidaqmx.constants import LineGrouping, Edge, AcquisitionType, WAIT_INFINITELY from motor_control import MotorControl tilt_types = [ ('a', 1, 9, 'Slow Counter Clockwise',), ('b', 2, 11, 'Fast Counter Clockwise',), ('c', 3, 12, 'Slow Clockwise' ,), ('d', 4, 14, 'Fast Clockwise' ,), ] with ExitStack() as stack: motor = MotorControl() stack.enter_context(motor) for tilt_type in tilt_types: print("tilt", tilt_type) with nidaqmx.Task() as task: sample_rate = 1000 batch_size = 3000 read_timeout = 4 task.timing.cfg_samp_clk_timing( sample_rate, source='', sample_mode=AcquisitionType.CONTINUOUS, samps_per_chan=batch_size, ) task.ai_channels.add_ai_voltage_chan("Dev6/ai8") motor.tilt(tilt_type[0]) time.sleep(1.75) motor.tilt('stop') data = task.read(batch_size, read_timeout) assert len(data) == 1 strobe = data[0] print("strobe max", max(strobe)) assert strobe[0] < 4 assert strobe[-1] < 4 assert any(x > 4 for x in strobe) input('press enter to continue') motor.close()
class TiltPlatform(AbstractContextManager): def __init__(self, *, mock: bool = False, delay_range: Tuple[float, float]): self.delay_range = delay_range self.motor = MotorControl(mock = mock) def __exit__(self, *exc): self.close() def stop(self): self.motor.tilt('stop') def close(self): self.motor.close() def tilt(self, tilt_type, water=False): water_duration = 0.15 tilt_duration = 1.75 try: tilt_name = {1: 'a', 2: 'b', 3: 'c', 4: 'd'}[tilt_type] except KeyError: raise ValueError("Invalid tilt type {}".format(tilt_type)) self.motor.tilt(tilt_name) time.sleep(tilt_duration) self.motor.tilt('stop') if water: self.motor.tilt('wateron') time.sleep(water_duration) self.motor.tilt('stop') # delay = ((randint(1,100))/100)+1.5 import random delay = random.uniform(*self.delay_range) time.sleep(delay)
class PsthTiltPlatform(AbstractContextManager): def __init__( self, *, baseline_recording: bool, save_template: bool = True, template_output_path, template_in_path, channel_dict, mock: bool = False, reward_enabled: bool, ): self.mock = mock self.save_template = save_template self.template_output_path = template_output_path self.template_in_path = template_in_path self.reward_enabled = reward_enabled if save_template: assert self.template_output_path is not None self.motor = MotorControl(mock=mock) self.motor.tilt('stop') self.motor_interrupt = MotorControl(port=1, mock=mock) self.motor_interrupt.tilt('stop') if mock: self.PL_SingleWFType = 0 self.PL_ExtEventType = 1 self.plex_client = None else: from pyplexclientts import PyPlexClientTSAPI, PL_SingleWFType, PL_ExtEventType dll_path = Path(__file__).parent / 'bin' client = PyPlexClientTSAPI(plexclient_dll_path=dll_path) client.init_client() self.PL_SingleWFType = PL_SingleWFType self.PL_ExtEventType = PL_ExtEventType self.plex_client = client # channel_dict = { # 1: [1], 2: [1,2], 3: [1,2], 4: [1,2], # 6: [1,2], 7: [1,2,3,4], 8: [1,2,3], # 9: [1,2,3], 13: [1,2,3,4], 14: [1,2], # 20: [1,2,3], 25: [1,2], 26: [1], 27: [1], 28: [1], # 31: [1], # 55: [1,2,3,4], # } pre_time = 0.0 post_time = 0.200 self._post_time = post_time bin_size = 0.020 self.baseline_recording = baseline_recording event_num_mapping = { 1: 1, 2: 2, 3: 3, 4: 4, 9: 1, 11: 2, 12: 3, 14: 4, } psth = Psth(channel_dict, pre_time, post_time, bin_size, event_num_mapping=event_num_mapping) if not baseline_recording: assert template_in_path is not None if template_in_path is not None: psth.loadtemplate(template_in_path) self.psth = psth self._mock_state = { '_get_ts': { 's': 'pending', 't': time.perf_counter(), } } self.no_spike_wait = False # time to wait after tilt event is recieved from plexon self.fixed_spike_wait_time = None # program fails after this amount of time if tilt isn't recieved from plexon self.fixed_spike_wait_timeout = None self.closed = False self.delay_range = (1.5, 2) def __exit__(self, *exc): self.close() def close(self, *, save_template=None): if self.closed == True: return self.closed = True if save_template is None: save_template = self.save_template self.motor.close() self.motor_interrupt.close() if not self.mock: self.plex_client.close_client() if save_template: self.psth.psthtemplate() self.psth.savetemplate(self.template_output_path) def _get_ts(self): if self.mock: for k, v in self.psth.channel_dict.items(): if v: channel = k unit = v[0] break class MockEvent: Channel: int Type: int TimeStamp: float time.sleep(0.050) # wait 50ms to maybe mimick plexon s = self._mock_state['_get_ts'] if s['s'] == 'pending': e = MockEvent() e.Type = self.PL_ExtEventType e.Channel = 257 e.Unit = unit e.TimeStamp = time.perf_counter() s['s'] = 'tilting' return [e] elif s['s'] == 'tilting': e = MockEvent() e.Type = self.PL_SingleWFType e.Channel = channel e.Unit = unit e.TimeStamp = time.perf_counter() s['s'] = 'pending' return [e] else: assert False else: res = self.plex_client.get_ts() return res def tilt(self, tilt_type, water=False, *, sham_result=None, delay=None): tilt_record = { 'system_time': time.perf_counter(), 'tilt_type': tilt_type, 'events': [], 'warnings': [], 'got_response': None, 'delay': None, 'decoder_result': None, 'decoder_result_source': None, 'predicted_tilt_type': None, } def add_event_to_record(event, *, ignored=None, relevent=None): rec = { 'system_time': time.perf_counter(), 'type': None, 'ignored': ignored, 'relevent': relevent, 'time': event.TimeStamp, 'channel': event.Channel, 'unit': event.Unit, } if event.Type == self.PL_SingleWFType: rec['type'] = 'spike' elif event.Type == self.PL_ExtEventType: rec['type'] = 'tilt' else: rec['type'] = event.Type tilt_record['events'].append(rec) water_duration = 0.15 punish_duration = 2 if tilt_type == 1: # data = tilt1 tilt_name = 'a' elif tilt_type == 2: # data = tilt3 tilt_name = 'b' elif tilt_type == 3: # data = tilt4 tilt_name = 'c' elif tilt_type == 4: # data = tilt6 tilt_name = 'd' else: raise ValueError("Invalid tilt type {}".format(tilt_type)) res = self._get_ts() for event in res: add_event_to_record(event, ignored=True) self.motor.tilt(tilt_name) send_tilt_time = time.time() found_event = False # track if a tilt has started yet collected_ts = False packets_since_tilt = 0 tilt_time = None tilt_plexon_time = None while (found_event == False or collected_ts == False) or self.fixed_spike_wait_time is not None: res = self._get_ts() if found_event: packets_since_tilt += 1 for t in res: # 50ms ? is_relevent = None if t.Type == self.PL_SingleWFType: is_relevent = self.psth.build_unit(t.Channel, t.Unit, t.TimeStamp) if is_relevent: if self.fixed_spike_wait_time or self.no_spike_wait: collected_ts = True elif tilt_plexon_time is not None and \ t.TimeStamp >= tilt_plexon_time + self._post_time: collected_ts = True elif t.Type == self.PL_ExtEventType: if t.Channel == 257 and found_event: warn_str = "WARNING: recieved a second tilt event" print(warn_str) tilt_record['warnings'].append(warn_str) is_relevent = False # tilt started if t.Channel == 257 and not found_event: print(('Event Ts: {}s Ch: {} Unit: {}').format( t.TimeStamp, t.Channel, t.Unit)) # print('event') self.psth.event(t.TimeStamp, t.Unit) found_event = True is_relevent = True tilt_time = time.time() tilt_plexon_time = t.TimeStamp add_event_to_record(t, relevent=is_relevent) if self.no_spike_wait or \ ( self.fixed_spike_wait_time is not None and tilt_time is not None and time.time() - tilt_time > self.fixed_spike_wait_time ): # don't wait for a spike if not found_event or not collected_ts: warn_str = "WARNING: no spike events found for trial. THIS SHOULD NOT HAPPEN. TELL DR MOXON" print(warn_str) tilt_record['warnings'].append(warn_str) break if self.fixed_spike_wait_timeout is not None and \ (time.time() - send_tilt_time > self.fixed_spike_wait_timeout): raise SpikeWaitTimeout(tilt_record) print('found event and collected ts') if tilt_time is not None: post_tilt_wait_time = time.time() - tilt_time else: post_tilt_wait_time = None print('post tilt wait time', post_tilt_wait_time, 'send', time.time() - send_tilt_time) # print('post send tilt time', time.time() - send_tilt_time) got_response = found_event and collected_ts tilt_record['got_response'] = got_response if got_response: self.psth.psth(True, self.baseline_recording) if not self.baseline_recording: self.psth.psth(False, self.baseline_recording) # ?if not self.baseline_recording and found_event and collected_ts: if not self.baseline_recording: if sham_result is not None: decoder_result = sham_result d_source = 'sham' predicted_tilt_type = None elif got_response: decoder_result = self.psth.decode() d_source = 'psth' predicted_tilt_type = self.psth.decoder_list[-1] else: print("skipping decode due to no spikes") decoder_result = True d_source = 'no_spikes' predicted_tilt_type = None tilt_record['decoder_result_source'] = d_source tilt_record['decoder_result'] = decoder_result tilt_record['predicted_tilt_type'] = predicted_tilt_type print(f"decode {decoder_result}") if decoder_result: if self.reward_enabled: self.motor_interrupt.tilt('reward') self.motor.tilt('wateron') time.sleep(water_duration) self.motor.tilt('stop') self.motor_interrupt.tilt('stop') else: self.motor_interrupt.tilt('punish') time.sleep(punish_duration) self.motor_interrupt.tilt('stop') # time.sleep(2) if delay is None: delay = random.uniform(*self.delay_range) tilt_record['delay'] = delay self.motor.tilt('stop') print(f'delay {delay:.2f}') time.sleep(delay) return tilt_record