Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BF: Fixes event code creation for BBTK. #1830

Merged
merged 1 commit into from May 18, 2018
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
97 changes: 69 additions & 28 deletions psychopy/hardware/bbtk/__init__.py
Expand Up @@ -252,26 +252,75 @@ def setResponse(self, sensor=None, outputPin = None, testDuration = None,
"""
Sets Digi Stim Capture and Response (DSCAR) for BBTK.

:param sensor: Takes string for single sensor, and tuple or list of strings for multiple sensors
:param sensor: Takes string for single sensor, and tuple or list of strings for multiple sensors, or
a list of lists (or tuples) for multiple events
:param outputPin: Takes string for single output, and tuple or list of strings for multiple outputs
:param testDuration: The duration of the testing session in seconds
:param responseTime: Time in seconds from stimulus capture that robotic actuator should respond
:param nTrials: Number of trials for testing session
:param setSmoothing: For use with CRT monitors - Defaults to False for common LCD screens. Mic smoothing active.
:param responseDuration: Time in seconds that robotic actuator should stay activated for each response
"""

def sensorValidator(sensor):
"""Sensor name validation."""
if type(sensor) is str:
sensor = [sensor,]
for sensors in sensor:
if sensors not in sensorDict.keys():
raise KeyError(
"{} is not a valid sensor name. Choose from the following: {}".format(sensors, list(sensorDict.keys())))
if len(sensor) != len(set(sensor)):
raise ValueError("Duplicate sensors are not allowed. Please use unique sensor names. E.g., {}"
.format(list(set(sensor))))

# Create sensor codes
def createSensorCode(sensor, eCodes, idx):
"""Creates event codes for trial list based on sensors requested."""
idx += 1
key = 'event' + str(idx) # Get dict key
eCodes[key] = '0' * 12
if type(sensor) is str:
sensor = [sensor, ]
if sensor is not None:
if type(sensor) in allowedListTypes:
for sensors in sensor:
eCodes[key] = eCodes[key][:sensorDict[sensors]] + '1' + eCodes[key][sensorDict[sensors] + 1:]
return eCodes

# Check sensor and output param casing
allowedListTypes = (type(()), type([]))
noneTypes = ['',None, 'None', 'none', False]
noneTypes = ['', None, 'None', 'none', False]
logging.info("Converting sensor and output names to lower case.")
if sensor in noneTypes:
sensor = None
if type(sensor) == tuple:
sensor = list(sensor)
if outputPin in noneTypes:
outputPin = None
if not sensor is None and type(sensor) in allowedListTypes:
if sensor is not None and any(type(elements) in allowedListTypes for elements in sensor): # list of lists
if not all(type(elements) in allowedListTypes for elements in sensor):
raise ValueError("For more than one event type, sensors must be list of lists.")
if any(len(elements) > 12 for elements in sensor):
raise ValueError("You can only set 12 sensor values for each event.")
if len(sensor) > 3:
raise ValueError("You can set sensors for a maximum of 3 events. "
"You have created {} events.".format(len(sensor)))
for idx, lists in enumerate(sensor):
if type(sensor[idx]) == type(()):
sensor[idx] = list(sensor[idx])
if type(sensor[idx]) == type([]):
for nextIdx, elements in enumerate(sensor[idx]):
sensor[idx][nextIdx] = sensor[idx][nextIdx].lower()
elif sensor is not None and type(sensor) in allowedListTypes: # Single list of sensors
if len(sensor) > 12:
raise ValueError("You can only set 12 sensor values for each event.")
sensor = [sensors.lower() for sensors in sensor]
elif not sensor is None:
elif sensor is not None: # Single string
sensor = sensor.lower()
if type(sensor) in allowedListTypes and len(sensor) > 12:
raise ValueError("You can only set 12 sensor values.")
# Check outputs
if not outputPin is None and type(outputPin) in allowedListTypes:
outputPin = [outputs.lower() for outputs in outputPin]
elif not outputPin is None:
Expand All @@ -287,18 +336,13 @@ def setResponse(self, sensor=None, outputPin = None, testDuration = None,
# Check sensor parameters
if sensor is None:
logging.info("Setting BBTK pattern matching to 'INDI' - respond to any trigger")
if type(sensor) in allowedListTypes and len(sensor) > 3:
raise ValueError("You can only set 3 sensor values. You have provided {} values.".format(len(sensor)))
if type(sensor) in allowedListTypes:
for sensors in sensor:
if not sensors in sensorDict.keys():
raise KeyError(
"{} is not a valid sensor name. Choose from the following: {}".format(sensors, list(sensorDict.keys())))
if len(sensor) != len(set(sensor)):
raise ValueError("Duplicate sensors are not allowed. Please use unique sensor names. E.g., {}"
.format(list(set(sensor))))
if not type(sensor) in allowedListTypes and not sensor in sensorDict.keys() and not sensor is None:
raise KeyError("{} is not a valid sensor name. Choose from the following: {}".format(sensor, list(sensorDict.keys())))

# Validate sensor names
if any(type(elements) == type([]) for elements in sensor):
for lists in sensor:
sensorValidator(lists)
else:
sensorValidator(sensor)
# Check output pin parameters
if outputPin is None:
raise ValueError("None values not accepted as outputs. OutputPin argument requires string e.g., 'TTLout1'.")
Expand All @@ -321,23 +365,20 @@ def setResponse(self, sensor=None, outputPin = None, testDuration = None,
raise ValueError("Please provide a time (in seconds) for the Robot Key Actuator to respond.")
if responseDuration is None:
raise ValueError("Please provide a duration (in seconds) for the Robot Key Actuator to respond.")
# Create sensor code
sensorCodes = dict(zip(['sensor1', 'sensor2', 'sensor3'], ['9' * 12, '9' * 12, '9' * 12]))
if not sensor is None:
if type(sensor) in allowedListTypes:
for n, sensors in enumerate(sensor, 1):
sensorCodes['{}{}'.format('sensor', str(n))] \
= '000000000000'[:sensorDict[sensors.lower()]] + '1' + '000000000000'[sensorDict[sensors.lower()]+1:]
else:
sensorCodes['sensor1'] = '000000000000'[:sensorDict[sensor.lower()]] \
+ '1' + '000000000000'[sensorDict[sensor.lower()] + 1:]
# Create event lists from sensors
sensorCodes = dict(zip(['event1', 'event2', 'event3'], ['9' * 12, '9' * 12, '9' * 12]))
if any(type(elements) == type([]) for elements in sensor):
for idx, lists in enumerate(sensor):
sensorCodes = createSensorCode(lists, sensorCodes, idx)
else:
sensorCodes = createSensorCode(sensor, sensorCodes, 0)
# Create output codes
if outputPin in allowedListTypes:
outputCode = '00000000'
for outputs in outputPin:
outputCode = outputCode[:outputDict[outputs.lower()]] + '1' + outputCode[outputDict[outputs.lower()]+1:]
outputCode = outputCode[:outputDict[outputs]] + '1' + outputCode[outputDict[outputs]+1:]
else:
outputCode = '00000000'[:outputDict[outputPin.lower()]] + '1' + '00000000'[outputDict[outputPin.lower()]+1:]
outputCode = '00000000'[:outputDict[outputPin]] + '1' + '00000000'[outputDict[outputPin]+1:]
# Create trialList for BBTK trials
trialList = '{input},{responseT},{output},{responseD}\r\n'.format(
input=','.join(sensorCodes.values()),
Expand Down