def build_header(self, run_asap=None, statemachine_body_size=0): message = [ord(SendMessageHeader.NEW_STATE_MATRIX)] message += [0 if run_asap is None else 1] message += [1 if self.use_255_back_signal else 0] return ArduinoTypes.get_uint8_array( message) + ArduinoTypes.get_uint16_array([statemachine_body_size])
def set_thresholds(self, thresholds): """ Sets the thresholds values to trigger the events. :ivar list(int) thresholds: List, in maximum, of 6 thresholds to trigger events. """ data = ArduinoTypes.get_uint8_array([self.COM_SETTHRESHOLDS, len(thresholds)]) data += ArduinoTypes.get_uint16_array([self.__degrees_2_pos(thresh) for thresh in thresholds]) self.arcom.write_array(data) return self.arcom.read_uint8() == 1
def _bpodcom_read_timestamps(self): data = self._arcom.read_bytes_array(12) n_hw_timer_cyles = ArduinoTypes.cvt_uint32(b''.join(data[:4])) trial_end_micros = ArduinoTypes.cvt_uint64(b''.join( data[4:12])) # / float(self.hardware.DEFAULT_FREQUENCY_DIVIDER) trial_end_timestamp = trial_end_micros / float( self.hardware.DEFAULT_FREQUENCY_DIVIDER) trial_time_from_micros = trial_end_timestamp - self.trial_start_timestamp trial_time_from_cycles = n_hw_timer_cyles / self.hardware.cycle_frequency discrepancy = abs(trial_time_from_micros - trial_time_from_cycles) * 1000 return trial_end_timestamp, discrepancy
def _bpodcom_enable_ports(self, hardware): """ Enable input ports on Bpod device :param list[int] inputs_enabled: list of inputs to be enabled (0 = disabled, 1 = enabled) :rtype: bool """ hardware.configure_inputs() logger.debug("Requesting ports enabling (%s)", SendMessageHeader.ENABLE_PORTS) logger.debug("Inputs enabled (%s): %s", len(hardware.inputs_enabled), hardware.inputs_enabled) bytes2send = ArduinoTypes.get_uint8_array( [ord(SendMessageHeader.ENABLE_PORTS)] + hardware.inputs_enabled) self._arcom.write_array(bytes2send) response = self._arcom.read_uint8() # type: int logger.debug("Response: %s", response) return True if response == ReceiveMessageHeader.ENABLE_PORTS_OK else False
def build_message_32_bits(self): """ Builds a 32 bit message to send to Bpod box :rtype: list(float) """ # TODO find how many global timers are used """ thirty_two_bit_message = [i * self.hardware.cycle_frequency for i in self.state_timers] + \ [i * self.hardware.cycle_frequency for i in self.global_timers.timers] + \ [i * self.hardware.cycle_frequency for i in self.global_timers.on_set_delays] + \ [i * self.hardware.cycle_frequency for i in self.global_timers.loop_intervals] + \ self.global_counters.thresholds""" used_timers = range(self.highest_used_global_timer) used_counters = range(self.highest_used_global_counter) thirty_two_bit_message = ( [t * self.hardware.cycle_frequency for t in self.state_timers] + [ self.global_timers.timers[i] * self.hardware.cycle_frequency for i in used_timers ] + [ self.global_timers.on_set_delays[i] * self.hardware.cycle_frequency for i in used_timers ] + [ self.global_timers.loop_intervals[i] * self.hardware.cycle_frequency for i in used_timers ] + [self.global_counters.thresholds[i] for i in used_counters]) return ArduinoTypes.get_uint32_array(thirty_two_bit_message)
def _bpodcom_resume_trial(self): """ Resumes ongoing trial (We recommend using computer-side pauses between trials, to keep data uniform) """ logger.debug("Resume trial") bytes2send = ArduinoTypes.get_uint8_array( [ord(SendMessageHeader.PAUSE_TRIAL), 1]) self._arcom.write_array(bytes2send)
def _bpodcom_send_softcode(self, softcode): """ Send soft code """ logger.debug("Send softcode") bytes2send = ArduinoTypes.get_uint8_array( [ord(SendMessageHeader.TRIGGER_SOFTCODE), softcode]) self._arcom.write_array(bytes2send)
def set_wrappoint(self, wrap_point): """ Sets wrap point (number of tics in a half-rotation) :ivar int wrap_point: number of tics in a half-rotation. """ ticks = self.__degrees_2_pos(wrap_point) self.arcom.write_array([self.COM_SETWRAPPOINT] + ArduinoTypes.get_uint16_array([ticks])) return self.arcom.read_uint8() == 1
def build_message_global_timer(self): message = [] for i in range(self.total_states_added): message += (self.global_timers.triggers_matrix[i], ) for i in range(self.total_states_added): message += (self.global_timers.cancels_matrix[i], ) for i in range(self.highest_used_global_timer): message += (self.global_timers.onset_matrix[i], ) if self.hardware.n_global_timers > 16: return ArduinoTypes.get_uint32_array(message) elif self.hardware.n_global_timers > 8: return ArduinoTypes.get_uint16_array(message) else: return ArduinoTypes.get_uint8_array(message)
def _bpodcom_send_byte_to_hardware_serial(self, channel_number, value): """ Send byte to hardware serial channel 1-3 :param int channel_number: :param int value: value to be written """ bytes2send = ArduinoTypes.get_uint8_array( [ord(SendMessageHeader.SEND_TO_HW_SERIAL), channel_number, value]) self._arcom.write_array(bytes2send)
def _bpodcom_manual_override_exec_event(self, event_index, event_data): """ Send soft code """ logger.debug("Manual override execute virtual event") bytes2send = ArduinoTypes.get_uint8_array([ ord(SendMessageHeader.MANUAL_OVERRIDE_EXEC_EVENT), event_index, event_data ]) self._arcom.write_array(bytes2send)
def _bpodcom_echo_softcode(self, softcode): """ Send soft code """ logger.debug("Echo softcode") self._arcom.write_char(SendMessageHeader.ECHO_SOFTCODE) self._arcom.write_char(softcode) bytes2send = ArduinoTypes.get_uint8_array( [ord(SendMessageHeader.ECHO_SOFTCODE), softcode]) self._arcom.write_array(bytes2send)
def set_position(self, degrees): """ Sets the current position in degrees. :ivar int degrees: current position in degrees. """ ticks = self.__degrees_2_pos(degrees) data = ArduinoTypes.get_uint8_array([self.COM_SETPOS]) data += ticks.to_bytes(2, byteorder='little', signed=True) self.arcom.write_array(data) return self.arcom.read_uint8() == 1
def _bpodcom_module_write(self, module_index, message, dtype=None): if dtype is None: dtype = ArduinoTypes.UINT8 if isinstance(message, str): message = [ord(c) for c in message] elif not isinstance(message, (list, np.ndarray)): message = [message] else: message = message msg = ArduinoTypes.get_array(message, dtype) if len(msg) > 64: raise BpodErrorException( "Error: module messages must be under 64 bytes per transmission" ) to_send = [ord(SendMessageHeader.WRITE_TO_MODULE), module_index + 1, len(msg)] to_send = ArduinoTypes.get_uint8_array(to_send) self._arcom.write_array(to_send + msg)
def _bpodcom_override_digital_hardware_state(self, channel_number, value): """ Manually set digital value on channel :param int channel_number: number of Bpod port :param int value: value to be written """ bytes2send = ArduinoTypes.get_uint8_array([ ord(SendMessageHeader.OVERRIDE_DIGITAL_HW_STATE), channel_number, value ]) self._arcom.write_array(bytes2send)
def _bpodcom_override_input_state(self, channel_number, value): """ Manually set digital value on channel :param int channel_number: number of Bpod port :param int value: value to be written """ logger.debug("Override input state") bytes2send = ArduinoTypes.get_uint8_array([ ord(SendMessageHeader.MANUAL_OVERRIDE_EXEC_EVENT), channel_number, value ]) self._arcom.write_array(bytes2send)
def _bpodcom_load_serial_message(self, serial_channel, message_id, serial_message, n_messages): """ Load serial message on channel :param TODO :rtype: bool """ # self.__bpodcom_check_com_ready() if isinstance(serial_channel, BpodModule): serial_channel = serial_channel.serial_port self.msg_id_list[message_id] = True if len(serial_message) > 3: raise BpodErrorException( 'Error: Serial messages cannot be more than 3 bytes in length.' ) if not (1 <= message_id <= 255): raise BpodErrorException( 'Error: Bpod can only store 255 serial messages (indexed 1-255). You used the message_id {0}' .format(message_id)) message_container = [ serial_channel - 1, n_messages, message_id, len(serial_message) ] + serial_message logger.debug("Requesting load serial message (%s)", SendMessageHeader.LOAD_SERIAL_MESSAGE) logger.debug("Message: %s", message_container) bytes2send = ArduinoTypes.get_uint8_array( [ord(SendMessageHeader.LOAD_SERIAL_MESSAGE)] + message_container) self._arcom.write_array(bytes2send) response = self._arcom.read_uint8() # type: int logger.debug("Confirmation: %s", response) return True if response == ReceiveMessageHeader.LOAD_SERIAL_MESSAGE_OK else False
def _bpodcom_set_sync_channel_and_mode(self, sync_channel, sync_mode): """ Request sync channel and sync mode configuration :param int sync_channel: 255 = no sync, otherwise set to a hardware channel number :param int sync_mode: 0 = flip logic every trial, 1 = every state :rtype: bool """ logger.debug("Requesting sync channel and mode (%s)", SendMessageHeader.SYNC_CHANNEL_MODE) bytes2send = ArduinoTypes.get_uint8_array([ ord(SendMessageHeader.SYNC_CHANNEL_MODE), sync_channel, sync_mode ]) self._arcom.write_array(bytes2send) response = self._arcom.read_uint8() # type: int logger.debug("Response: %s", response) return True if response == ReceiveMessageHeader.SYNC_CHANNEL_MODE_OK else False
def _bpodcom_enable_ports(self, hardware): """ Enable input ports on Bpod device :param list[int] inputs_enabled: list of inputs to be enabled (0 = disabled, 1 = enabled) :rtype: bool """ ###### set inputs enabled or disabled ####################################################### hardware.inputs_enabled = [0] * len(hardware.inputs) for j, i in enumerate(hardware.bnc_inputports_indexes): hardware.inputs_enabled[i] = settings.BPOD_BNC_PORTS_ENABLED[j] for j, i in enumerate(hardware.wired_inputports_indexes): hardware.inputs_enabled[i] = settings.BPOD_WIRED_PORTS_ENABLED[j] for j, i in enumerate(hardware.behavior_inputports_indexes): hardware.inputs_enabled[i] = settings.BPOD_BEHAVIOR_PORTS_ENABLED[ j] ############################################################################################# logger.debug("Requesting ports enabling (%s)", SendMessageHeader.ENABLE_PORTS) logger.debug("Inputs enabled (%s): %s", len(hardware.inputs_enabled), hardware.inputs_enabled) bytes2send = ArduinoTypes.get_uint8_array( [ord(SendMessageHeader.ENABLE_PORTS)] + hardware.inputs_enabled) self._arcom.write_array(bytes2send) response = self._arcom.read_uint8() # type: int logger.debug("Response: %s", response) return True if response == ReceiveMessageHeader.ENABLE_PORTS_OK else False
def _bpodcom_get_modules_info(self, hardware): bpod_modules = BpodModules(self) # type: BpodModules input_modules = [inp for inp in hardware.inputs if inp == "U"] n_modules = len(input_modules) n_serial_events = int(hardware.max_serial_events / (n_modules + 1)) self._arcom.write_char(SendMessageHeader.GET_MODULES) time.sleep(0.3) modules_requested_events = np.array([0] * n_modules) names = {} if self._arcom.bytes_available() > 1: for i in range(n_modules): connected = self._arcom.read_uint8() == 1 firmware_version = None module_name = None events_names = [] if connected: firmware_version = self._arcom.read_uint32() name_length = self._arcom.read_uint8() name_string = self._arcom.read_char_array(name_length) name_string = "".join(name_string) if name_string not in names: names[name_string] = 0 names[name_string] += 1 module_name = name_string + str(names[name_string]) flag = self._arcom.read_uint8() while flag == 1: # has more info to be read param_type = self._arcom.read_uint8() if param_type == ReceiveMessageHeader.MODULE_REQUESTED_EVENT: modules_requested_events[i] = self._arcom.read_uint8() elif param_type == ReceiveMessageHeader.MODULE_EVENT_NAMES: n_event_names = self._arcom.read_uint8() for _ in range(n_event_names): n_chars = self._arcom.read_uint8() event_name = self._arcom.read_char_array(n_chars) events_names.append("".join(event_name)) flag = self._arcom.read_uint8() bpod_modules += BpodModules.create_module( connected, module_name, firmware_version, events_names, n_serial_events, serial_port=i + 1, ) if ( modules_requested_events.sum() + n_serial_events ) > hardware.max_serial_events: raise BpodErrorException( "Error: Connected modules requested too many events." ) for i, module in enumerate(bpod_modules): if module.connected: if modules_requested_events[i] > module.n_serial_events: n_to_reassign = modules_requested_events[i] - module.n_serial_events module.n_serial_events = modules_requested_events[i] else: n_to_reassign = 0 index = n_modules - 1 while n_to_reassign > 0: if bpod_modules[index].n_serial_events >= n_to_reassign: bpod_modules[index].n_serial_events = ( bpod_modules[index].n_serial_events - n_to_reassign ) n_to_reassign = 0 else: n_to_reassign = ( n_to_reassign - bpod_modules[index].n_serial_events ) bpod_modules[index].n_serial_events = 0 index -= 1 n_serial_events_array = [m.n_serial_events for m in bpod_modules] n_soft_codes = hardware.max_serial_events - sum(n_serial_events_array) bytes2send = ArduinoTypes.get_uint8_array( [ord("%")] + n_serial_events_array + [n_soft_codes] ) self._arcom.write_array(bytes2send) res = self._arcom.read_uint8() if not res: raise BpodErrorException( "Error: Failed to configure module event assignment." ) return bpod_modules
def build_message(self): """ Builds state machine to send to Bpod box :rtype: list(int) """ self.highest_used_global_counter = self.global_counters.get_max_index_used( ) self.highest_used_global_timer = self.global_timers.get_max_index_used( ) self.highest_used_global_condition = self.conditions.get_max_index_used( ) self.highest_used_global_counter = ( 0 if self.highest_used_global_counter is None else self.highest_used_global_counter + 1) self.highest_used_global_timer = ( 0 if self.highest_used_global_timer is None else self.highest_used_global_timer + 1) self.highest_used_global_condition = ( 0 if self.highest_used_global_condition is None else self.highest_used_global_condition + 1) message = [ self.total_states_added, self.highest_used_global_timer, self.highest_used_global_counter, self.highest_used_global_condition, ] # STATE TIMER MATRIX # Send state timer transitions (for all states) tmp = [] for i in range(self.total_states_added): tmp += [ self.total_states_added if math.isnan( self.state_timer_matrix[i]) else self.state_timer_matrix[i] ] message += tmp logger.debug("STATE TIMER MATRIX: %s", tmp) # INPUT MATRIX # Send event-triggered transitions (where they are different from default) tmp = [] for i in range(self.total_states_added): state_transitions = self.input_matrix[i] n_transitions = len(state_transitions) tmp += [n_transitions] for transition in state_transitions: tmp += [transition[0]] dest_state = transition[1] tmp += [ self.total_states_added if math.isnan(dest_state) else dest_state ] message += tmp logger.debug("INPUT MATRIX: %s", tmp) # OUTPUT MATRIX # Send hardware states (where they are different from default) tmp = [] for i in range(self.total_states_added): hw_state = self.output_matrix[i] hw_state = [ evt for evt in hw_state if evt[0] < self.hardware.channels.events_positions.globalTimerTrigger ] n_differences = len(hw_state) tmp += [n_differences] for hw_conf in hw_state: tmp += hw_conf[:2] message += tmp logger.debug("OUTPUT MATRIX: %s", tmp) # GLOBAL_TIMER_START_MATRIX # Send global timer-start triggered transitions (where they are different from default) tmp = [] for i in range(self.total_states_added): state_transitions = self.global_timers.start_matrix[i] n_transitions = len(state_transitions) tmp += [n_transitions] for transition in state_transitions: dest_state = transition[1] tmp += [ transition[0] - self.hardware.channels.events_positions.globalTimerStart ] tmp += [ self.total_states_added if math.isnan(dest_state) else dest_state ] message += tmp logger.debug("GLOBAL_TIMER_START_MATRIX: %s", tmp) # GLOBAL_TIMER_END_MATRIX # Send global timer-end triggered transitions (where they are different from default) tmp = [] for i in range(self.total_states_added): state_transitions = self.global_timers.end_matrix[i] n_transitions = len(state_transitions) tmp += [n_transitions] for transition in state_transitions: dest_state = transition[1] tmp += [ transition[0] - self.hardware.channels.events_positions.globalTimerEnd ] tmp += [ self.total_states_added if math.isnan(dest_state) else dest_state ] message += tmp logger.debug("GLOBAL_TIMER_END_MATRIX: %s", tmp) # GLOBAL_COUNTER_MATRIX # Send global counter triggered transitions (where they are different from default) tmp = [] for i in range(self.total_states_added): state_transitions = self.global_counters.matrix[i] n_transitions = len(state_transitions) tmp += [n_transitions] for transition in state_transitions: dest_state = transition[1] tmp += [ transition[0] - self.hardware.channels.events_positions.globalCounter ] tmp += [ self.total_states_added if math.isnan(dest_state) else dest_state ] message += tmp logger.debug("GLOBAL_COUNTER_MATRIX: %s", tmp) # CONDITION_MATRIX # Send condition triggered transitions (where they are different from default) tmp = [] for i in range(self.total_states_added): state_transitions = self.conditions.matrix[i] n_transitions = len(state_transitions) tmp += [n_transitions] for transition in state_transitions: dest_state = transition[1] tmp += [ transition[0] - self.hardware.channels.events_positions.condition ] tmp += [ self.total_states_added if math.isnan(dest_state) else dest_state ] message += tmp logger.debug("CONDITION_MATRIX: %s", tmp) # GLOBAL_TIMER_CHANNELS tmp = [] for i in range(self.highest_used_global_timer): tmp += [self.global_timers.channels[i]] message += tmp logger.debug("GLOBAL_TIMER_CHANNELS: %s", tmp) # GLOBAL_TIMER_ON_MESSAGES tmp = [] for i in range(self.highest_used_global_timer): v = self.global_timers.on_messages[i] tmp += [255 if v == 0 else v] message += tmp logger.debug("GLOBAL_TIMER_ON_MESSAGES: %s", tmp) # GLOBAL_TIMER_OFF_MESSAGES tmp = [] for i in range(self.highest_used_global_timer): v = self.global_timers.off_messages[i] tmp += [255 if v == 0 else v] message += tmp logger.debug("GLOBAL_TIMER_OFF_MESSAGES: %s", tmp) # GLOBAL_TIMER_LOOP_MODE tmp = [] for i in range(self.highest_used_global_timer): tmp += [self.global_timers.loop_mode[i]] message += tmp logger.debug("GLOBAL_TIMER_LOOP_MODE: %s", tmp) # GLOBAL_TIMER_EVENTS tmp = [] for i in range(self.highest_used_global_timer): tmp += [self.global_timers.send_events[i]] message += tmp logger.debug("GLOBAL_TIMER_EVENTS: %s", tmp) # GLOBAL_COUNTER_ATTACHED_EVENTS tmp = [] for i in range(self.highest_used_global_counter): tmp += [self.global_counters.attached_events[i]] message += tmp logger.debug("GLOBAL_COUNTER_ATTACHED_EVENTS: %s", tmp) # CONDITIONS_CHANNELS tmp = [] for i in range(self.highest_used_global_condition): tmp += [self.conditions.channels[i]] message += tmp logger.debug("CONDITIONS_CHANNELS: %s", tmp) # CONDITIONS VALUES tmp = [] for i in range(self.highest_used_global_condition): tmp += [self.conditions.values[i]] message += tmp logger.debug("CONDITIONS VALUES: %s", tmp) # GLOBAL_COUNTER_RESETS tmp = [] for i in range(self.total_states_added): tmp += [self.global_counters.reset_matrix[i]] message += tmp logger.debug("GLOBAL_COUNTER_RESETS: %s", tmp) self.state_timers = self.state_timers[:self.total_states_added] return ArduinoTypes.get_uint8_array(message)
def _bpodcom_get_trial_timestamp_start(self): data = self._arcom.read_bytes_array(8) self.trial_start_micros = ArduinoTypes.cvt_uint64(b''.join(data)) return self.trial_start_micros / float( self.hardware.DEFAULT_FREQUENCY_DIVIDER)