예제 #1
0
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)
예제 #3
0
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