Exemple #1
0
    def _read_bool(self, channel, invert=False, event=None, **kwargs):
        """ Read a boolean value from a channel or group of channels

        Parameters
        ----------
        channel: string
            a channel or group of channels that will all be read at the same time
        invert: bool
            whether or not to invert the read value
        event: dict
            a dictionary of event information to emit after a True reading

        Returns
        -------
        The value read from the hardware
        """
        if channel not in self.tasks:
            raise NIDAQmxError("Channel(s) %s not yet configured" %
                               str(channel))

        task = self.tasks[channel]
        task.start()
        # while task.get_samples_per_channel_acquired() == 0:
        #     pass
        value, bits_per_sample = task.read(1)
        value = value[0, 0]
        task.stop()
        if invert:
            value = 1 - value
        value = bool(value == 1)
        if value:
            events.write(event)

        return value
Exemple #2
0
    def _queue_wav(self, wav_file, start=False, event=None, **kwargs):
        """ Queue the wav file for playback

        Parameters
        ----------
        wav_file: string
            Path to the wave file to load
        start: bool
            Whether or not to immediately start playback
        event: dict
            a dictionary of event information to emit just before playback
        """

        if self.wf is not None:
            self._stop_wav()

        events.write(event)
        logger.debug("Queueing wavfile %s" % wav_file)
        self._wav_data = self._load_wav(wav_file)

        if self._analog_event_handler is not None:
            # Get the string of (scaled) bits from the event handler
            bit_string = self._analog_event_handler.to_bit_sequence(event)

            # multi-channel outputs need to be of shape nsamples x nchannels
            if len(self._wav_data.shape) == 1:
                values = self._wav_data.reshape((-1, 1))
            else:
                values = self._wav_data

            # Add a channel of all zeros
            self._wav_data = np.hstack([values, np.zeros((values.shape[0], 1))])
            # Place the bit string at the start
            self._wav_data[:len(bit_string), -1] = bit_string
        self._get_stream(start=start, **kwargs)
Exemple #3
0
    def _read_analog(self, channel, nsamples, event=None, **kwargs):
        """ Read from a channel or group of channels for the specified number of
        samples.

        Parameters
        ----------
        channel: string
            a channel or group of channels that will be read at the same time
        nsamples: int
            the number of samples to read
        event: dict
            a dictionary of event information to emit after reading

        Returns
        -------
        a numpy array of the data that was read
        """

        if channel not in self.tasks:
            raise NIDAQmxError("Channel(s) %s not yet configured" % str(channel))

        task = self.tasks[channel]
        task.configure_timing_sample_clock(source=self.clock_channel,
                                           rate=self.samplerate,
                                           sample_mode="finite",
                                           samples_per_channel=nsamples)
        values = task.read(nsamples)
        events.write(event)
        return values
Exemple #4
0
    def _read_analog(self, channel, nsamples, event=None, **kwargs):
        """ Read from a channel or group of channels for the specified number of
        samples.

        Parameters
        ----------
        channel: string
            a channel or group of channels that will be read at the same time
        nsamples: int
            the number of samples to read
        event: dict
            a dictionary of event information to emit after reading

        Returns
        -------
        a numpy array of the data that was read
        """

        if channel not in self.tasks:
            raise NIDAQmxError("Channel(s) %s not yet configured" %
                               str(channel))

        task = self.tasks[channel]
        task.configure_timing_sample_clock(source=self.clock_channel,
                                           rate=self.samplerate,
                                           sample_mode="finite",
                                           samples_per_channel=nsamples)
        values = task.read(nsamples)
        events.write(event)
        return values
Exemple #5
0
    def _read_bool(self, channel, invert=False, event=None, **kwargs):
        """ Read a boolean value from a channel or group of channels

        Parameters
        ----------
        channel: string
            a channel or group of channels that will all be read at the same time
        invert: bool
            whether or not to invert the read value
        event: dict
            a dictionary of event information to emit after a True reading

        Returns
        -------
        The value read from the hardware
        """
        if channel not in self.tasks:
            raise NIDAQmxError("Channel(s) %s not yet configured" % str(channel))

        task = self.tasks[channel]
        task.start()
        # while task.get_samples_per_channel_acquired() == 0:
        #     pass
        value, bits_per_sample = task.read(1)
        value = value[0, 0]
        task.stop()
        if invert:
            value = 1 - value
        value = bool(value == 1)
        if value:
            events.write(event)

        return value
Exemple #6
0
    def _write_analog(self,
                      channel,
                      values,
                      is_blocking=False,
                      event=None,
                      **kwargs):
        """ Write a numpy array of float64 values to the buffer on a channel or
        group of channels

        Parameters
        ----------
        channel: string
            a channel or group of channels that will all be written to at the same time
        values: numpy array of float64 values
            values to write to the hardware. Should be of dimension nchannels x nsamples.
        is_blocking: bool
            whether or not to block execution until all samples are written to the hardware
        event: dict
            a dictionary of event information to emit just before writing

        Returns
        -------
        True
        """

        if channel not in self.tasks:
            raise NIDAQmxError("Channel(s) %s not yet configured" %
                               str(channel))

        task = self.tasks[channel]
        task.stop()
        task.configure_timing_sample_clock(source=self.clock_channel,
                                           rate=self.samplerate,
                                           sample_mode="finite",
                                           samples_per_channel=values.shape[0])

        if self._analog_event_handler is not None:
            # Get the string of (scaled) bits from the event handler
            bit_string = self._analog_event_handler.to_bit_sequence(event)

            # multi-channel outputs need to be of shape nsamples x nchannels
            if len(values.shape) == 1:
                values = values.reshape((-1, 1))

            # Add a channel of all zeros
            values = np.hstack([values, np.zeros((values.shape[0], 1))])
            # Place the bit string at the start
            values[:len(bit_string), -1] = bit_string

        # Write the values to the nidaq buffer
        # I think we might want to set layout='group_by_scan_number' in .write()
        task.write(values, auto_start=False)
        events.write(event)
        task.start()
        if is_blocking:
            task.wait_until_done()
            task.stop()

        return True
Exemple #7
0
    def _write_analog(self, channel, values, is_blocking=False, event=None,
                      **kwargs):
        """ Write a numpy array of float64 values to the buffer on a channel or
        group of channels

        Parameters
        ----------
        channel: string
            a channel or group of channels that will all be written to at the same time
        values: numpy array of float64 values
            values to write to the hardware. Should be of dimension nchannels x nsamples.
        is_blocking: bool
            whether or not to block execution until all samples are written to the hardware
        event: dict
            a dictionary of event information to emit just before writing

        Returns
        -------
        True
        """

        if channel not in self.tasks:
            raise NIDAQmxError("Channel(s) %s not yet configured" % str(channel))

        task = self.tasks[channel]
        task.stop()
        task.configure_timing_sample_clock(source=self.clock_channel,
                                           rate=self.samplerate,
                                           sample_mode="finite",
                                           samples_per_channel=values.shape[0])

        if self._analog_event_handler is not None:
            # Get the string of (scaled) bits from the event handler
            bit_string = self._analog_event_handler.to_bit_sequence(event)

            # multi-channel outputs need to be of shape nsamples x nchannels
            if len(values.shape) == 1:
                values = values.reshape((-1, 1))

            # Add a channel of all zeros
            values = np.hstack([values, np.zeros((values.shape[0], 1))])
            # Place the bit string at the start
            values[:len(bit_string), -1] = bit_string

        # Write the values to the nidaq buffer
        # I think we might want to set layout='group_by_scan_number' in .write()
        task.write(values, auto_start=False)
        events.write(event)
        task.start()
        if is_blocking:
            task.wait_until_done()
            task.stop()

        return True
Exemple #8
0
 def _stop_wav(self, event=None, **kwargs):
     try:
         logger.debug("Attempting to close pyaudio stream")
         events.write(event)
         self.stream.close()
         logger.debug("Stream closed")
     except AttributeError:
         self.stream = None
     try:
         self.wf.close()
     except AttributeError:
         self.wf = None
Exemple #9
0
 def _stop_wav(self, event=None, **kwargs):
     try:
         logger.debug("Attempting to close pyaudio stream")
         events.write(event)
         self.stream.close()
         logger.debug("Stream closed")
     except AttributeError:
         self.stream = None
     try:
         self.wf.close()
     except AttributeError:
         self.wf = None
Exemple #10
0
    def _read_bool(self, channel, invert=False, event=None, **kwargs):
        """ Read a value from the specified channel

        Parameters
        ----------
        channel: int
            the channel from which to read
        invert: bool
            whether or not to invert the read value

        Returns
        -------
        bool:
            the value read from the hardware

        Raises
        ------
        ArduinoException
            Reading from the device failed.
        """

        if channel not in self._state:
            raise InterfaceError("Channel %d is not configured on device %s" % (channel, self.device_name))

        if self.device.inWaiting() > 0: # There is currently data in the input buffer
            self.device.flushInput()
        self.device.write(self._make_arg(channel, 0))
        # Also need to make sure self.device.read() returns something that ord can work with. Possibly except TypeError
        while True:
            try:
                v = ord(self.device.read())
                break
                # serial.SerialException("Testing")
            except serial.SerialException:
            # This is to make it robust in case it accidentally disconnects or you try to access the arduino in
            # multiple ways
                pass
            except TypeError:
                ArduinoException("Could not read from arduino device")

        logger.debug("Read value of %d from channel %d on %s" % (v, channel, self))
        if v in [0, 1]:
            if invert:
                v = 1 - v
            value = v == 1
            if value:
                events.write(event)
            return value
        else:
            logger.error("Device %s returned unexpected value of %d on reading channel %d" % (self, v, channel))
Exemple #11
0
    def _play_wav(self, is_blocking=False, event=None, **kwargs):
        """ Play the data that is currently in the buffer

        Parameters
        ----------
        is_blocking: bool
            Whether or not to play the sound in blocking mode
        event: dict
            a dictionary of event information to emit just before playback
        """

        logger.debug("Playing wavfile")
        events.write(event)
        self.stream.start()
        if is_blocking:
            self.wait_until_done()
Exemple #12
0
    def _play_wav(self, is_blocking=False, event=None, **kwargs):
        """ Play the data that is currently in the buffer

        Parameters
        ----------
        is_blocking: bool
            Whether or not to play the sound in blocking mode
        event: dict
            a dictionary of event information to emit just before playback
        """

        logger.debug("Playing wavfile")
        events.write(event)
        self.stream.start()
        if is_blocking:
            self.wait_until_done()
Exemple #13
0
    def _write_bool(self, channel, value, event=None, **kwargs):
        '''Write a value to the specified channel
        :param channel: the channel to write to
        :param value: the value to write
        :return: value written if succeeded
        '''

        if channel not in self._state:
            raise InterfaceError("Channel %d is not configured on device %s" % (channel, self))

        logger.debug("Writing %s to device %s, channel %d" % (value, self, channel))
        events.write(event)
        if value:
            s = self.device.write(self._make_arg(channel, 1))
        else:
            s = self.device.write(self._make_arg(channel, 2))
        if s:
            return value
        else:
            raise InterfaceError('Could not write to serial device %s, channel %d' % (self.device, channel))
Exemple #14
0
    def _stop_wav(self, event=None, **kwargs):
        """ Stop the current playback and clear the buffer

        Parameters
        ----------
        event: dict
            a dictionary of event information to emit just before stopping
        """

        try:
            logger.debug("Attempting to close stream")
            events.write(event)
            self.stream.stop()
            logger.debug("Stream closed")
        except AttributeError:
            self.stream = None

        try:
            self.wf.close()
        except AttributeError:
             self.wf = None

        self._wav_data = None
Exemple #15
0
    def _stop_wav(self, event=None, **kwargs):
        """ Stop the current playback and clear the buffer

        Parameters
        ----------
        event: dict
            a dictionary of event information to emit just before stopping
        """

        try:
            logger.debug("Attempting to close stream")
            events.write(event)
            self.stream.stop()
            logger.debug("Stream closed")
        except AttributeError:
            self.stream = None

        try:
            self.wf.close()
        except AttributeError:
            self.wf = None

        self._wav_data = None
Exemple #16
0
    def _write_bool(self, channel, value, event=None, **kwargs):
        """ Write a boolean value to a channel or group of channels

        Parameters
        ----------
        channel: string
            a channel or group of channels that will all be written to at the same time
        value: bool or boolean array
            value to write to the hardware
        event: dict
            a dictionary of event information to emit just before writing

        Returns
        -------
        True
        """
        if channel not in self.tasks:
            raise NIDAQmxError("Channel(s) %s not yet configured" % str(channel))
        task = self.tasks[channel]
        events.write(event)
        task.write(value, auto_start=True)

        return True
Exemple #17
0
    def _write_bool(self, channel, value, event=None, **kwargs):
        """ Write a boolean value to a channel or group of channels

        Parameters
        ----------
        channel: string
            a channel or group of channels that will all be written to at the same time
        value: bool or boolean array
            value to write to the hardware
        event: dict
            a dictionary of event information to emit just before writing

        Returns
        -------
        True
        """
        if channel not in self.tasks:
            raise NIDAQmxError("Channel(s) %s not yet configured" %
                               str(channel))
        task = self.tasks[channel]
        events.write(event)
        task.write(value, auto_start=True)

        return True
Exemple #18
0
    def _queue_wav(self, wav_file, start=False, event=None, **kwargs):
        """ Queue the wav file for playback

        Parameters
        ----------
        wav_file: string
            Path to the wave file to load
        start: bool
            Whether or not to immediately start playback
        event: dict
            a dictionary of event information to emit just before playback
        """

        if self.wf is not None:
            self._stop_wav()

        events.write(event)
        logger.debug("Queueing wavfile %s" % wav_file)
        self._wav_data = self._load_wav(wav_file)

        if self._analog_event_handler is not None:
            # Get the string of (scaled) bits from the event handler
            bit_string = self._analog_event_handler.to_bit_sequence(event)

            # multi-channel outputs need to be of shape nsamples x nchannels
            if len(self._wav_data.shape) == 1:
                values = self._wav_data.reshape((-1, 1))
            else:
                values = self._wav_data

            # Add a channel of all zeros
            self._wav_data = np.hstack(
                [values, np.zeros((values.shape[0], 1))])
            # Place the bit string at the start
            self._wav_data[:len(bit_string), -1] = bit_string
        self._get_stream(start=start, **kwargs)
Exemple #19
0
    def _poll(self, channel=None, invert=False,
              last_value=False, suppress_longpress=False,
              timeout=None, wait=None, event=None, *args, **kwargs):
        """ Runs a loop, querying for the boolean input to return True.

        Parameters
        ----------
        channel:
            default channel argument to pass to _read_bool()
        invert: bool
            whether or not to invert the read value
        last_value: bool
            if the last read value was True. Necessary to suppress longpresses
        suppress_longpress: bool
            if True, attempts to suppress returning immediately if the button is still being pressed since the last call. If last_value is True, then it waits until the interface reads a single False value before allowing it to return.
        timeout: float
            the time, in seconds, until polling times out. Defaults to no timeout.
        wait: float
            the time, in seconds, to wait between subsequent reads (default no wait).

        Returns
        -------
        timestamp of True read or None if timed out
        """

        logger.debug("Begin polling from device %s" % self.device_name)
        if timeout is not None:
            start = time.time()

        if channel not in self.tasks:
            raise NIDAQmxError("Channel(s) %s not yet configured" % str(channel))

        task = self.tasks[channel]
        task.start()
        while True:
            # Read the value - cannot use _read_bool because it must start and stop the task each time.
            value, bits_per_sample = task.read(1)
            value = value[0, 0]
            if invert:
                value = 1 - value
            value = bool(value == 1)
            if value:
                events.write(event)

            if not isinstance(value, bool):
                task.stop()
                raise ValueError("Polling for bool returned something that was not a bool")
            if value is True:

                if (last_value is False) or (suppress_longpress is False):
                    logger.debug("Input detected. Returning")
                    task.stop()
                    return datetime.datetime.now()
            else:
                last_value = False

            if timeout is not None:
                if time.time() - start >= timeout:
                    logger.debug("Polling timed out. Returning")
                    task.stop()
                    return None

            if wait is not None:
                utils.wait(wait)
Exemple #20
0
    def run(self):
        """ Runs the trial

        Summary
        -------
        The main structure is as follows:

        Get stimulus -> Initiate trial -> Play stimulus -> Receive response ->
        Consequate response -> Finish trial -> Save data.

        The stimulus, response and consequate stages are broken into pre, main,
        and post stages. Only use the stages you need in your experiment.
        """

        self.experiment.this_trial = self

        # Get the stimulus
        self.stimulus = self.condition.get()

        # Any pre-trial logging / computations
        self.experiment.trial_pre()

        # Emit trial event
        self.event.update(action="start", metadata=str(self.index))
        events.write(self.event)

        # Record the trial time
        self.time = dt.datetime.now()

        # Perform stimulus playback
        self.experiment.stimulus_pre()
        self.experiment.stimulus_main()
        self.experiment.stimulus_post()

        # Evaluate subject's response
        self.experiment.response_pre()
        self.experiment.response_main()
        self.experiment.response_post()

        # Consequate the response with a reward, punishment or neither
        if self.response == self.condition.response:
            self.correct = True
            if self.condition.is_rewarded and self.block.reinforcement.consequate(
                    self):
                self.reward = True
                self.experiment.reward_pre()
                self.experiment.reward_main()
                self.experiment.reward_post()
        else:
            self.correct = False
            if self.condition.is_punished and self.block.reinforcement.consequate(
                    self):
                self.punish = True
                self.experiment.punish_pre()
                self.experiment.punish_main()
                self.experiment.punish_post()

        # Emit trial end event
        self.event.update(action="end", metadata=str(self.index))
        events.write(self.event)

        # Finalize trial
        self.experiment.trial_post()

        # Store trial data
        self.experiment.subject.store_data(self)

        # Update session schedulers
        self.experiment.session.update()

        if self.experiment.check_session_schedule() is False:
            logger.debug("Session has run long enough. Ending")
            raise EndSession
Exemple #21
0
 def _play_wav(self, event=None, **kwargs):
     logger.debug("Playing wavfile")
     events.write(event)
     self.stream.start_stream()
Exemple #22
0
    def run(self):
        """ Runs the trial

        Summary
        -------
        The main structure is as follows:

        Get stimulus -> Initiate trial -> Play stimulus -> Receive response ->
        Consequate response -> Finish trial -> Save data.

        The stimulus, response and consequate stages are broken into pre, main,
        and post stages. Only use the stages you need in your experiment.
        """

        self.experiment.this_trial = self

        # Get the stimulus
        self.stimulus = self.condition.get()

        # Any pre-trial logging / computations
        self.experiment.trial_pre()

        # Emit trial event
        self.event.update(action="start", metadata=str(self.index))
        events.write(self.event)

        # Record the trial time
        self.time = dt.datetime.now()

        # Perform stimulus playback
        self.experiment.stimulus_pre()
        self.experiment.stimulus_main()
        self.experiment.stimulus_post()

        # Evaluate subject's response
        self.experiment.response_pre()
        self.experiment.response_main()
        self.experiment.response_post()

        # Consequate the response with a reward, punishment or neither
        if self.response == self.condition.response:
            self.correct = True
            if self.condition.is_rewarded and self.block.reinforcement.consequate(self):
                self.reward = True
                self.experiment.reward_pre()
                self.experiment.reward_main()
                self.experiment.reward_post()
        else:
            self.correct = False
            if self.condition.is_punished and self.block.reinforcement.consequate(self):
                self.punish = True
                self.experiment.punish_pre()
                self.experiment.punish_main()
                self.experiment.punish_post()

        # Emit trial end event
        self.event.update(action="end", metadata=str(self.index))
        events.write(self.event)

        # Finalize trial
        self.experiment.trial_post()

        # Store trial data
        self.experiment.subject.store_data(self)

        # Update session schedulers
        self.experiment.session.update()

        if self.experiment.check_session_schedule() is False:
            logger.debug("Session has run long enough. Ending")
            raise EndSession
Exemple #23
0
 def _play_wav(self, event=None, **kwargs):
     logger.debug("Playing wavfile")
     events.write(event)
     self.stream.start_stream()
Exemple #24
0
    def _poll(self,
              channel=None,
              invert=False,
              last_value=False,
              suppress_longpress=False,
              timeout=None,
              wait=None,
              event=None,
              *args,
              **kwargs):
        """ Runs a loop, querying for the boolean input to return True.

        Parameters
        ----------
        channel:
            default channel argument to pass to _read_bool()
        invert: bool
            whether or not to invert the read value
        last_value: bool
            if the last read value was True. Necessary to suppress longpresses
        suppress_longpress: bool
            if True, attempts to suppress returning immediately if the button is still being pressed since the last call. If last_value is True, then it waits until the interface reads a single False value before allowing it to return.
        timeout: float
            the time, in seconds, until polling times out. Defaults to no timeout.
        wait: float
            the time, in seconds, to wait between subsequent reads (default no wait).

        Returns
        -------
        timestamp of True read or None if timed out
        """

        logger.debug("Begin polling from device %s" % self.device_name)
        if timeout is not None:
            start = time.time()

        if channel not in self.tasks:
            raise NIDAQmxError("Channel(s) %s not yet configured" %
                               str(channel))

        task = self.tasks[channel]
        task.start()
        while True:
            # Read the value - cannot use _read_bool because it must start and stop the task each time.
            value, bits_per_sample = task.read(1)
            value = value[0, 0]
            if invert:
                value = 1 - value
            value = bool(value == 1)
            if value:
                events.write(event)

            if not isinstance(value, bool):
                task.stop()
                raise ValueError(
                    "Polling for bool returned something that was not a bool")
            if value is True:

                if (last_value is False) or (suppress_longpress is False):
                    logger.debug("Input detected. Returning")
                    task.stop()
                    return datetime.datetime.now()
            else:
                last_value = False

            if timeout is not None:
                if time.time() - start >= timeout:
                    logger.debug("Polling timed out. Returning")
                    task.stop()
                    return None

            if wait is not None:
                utils.wait(wait)