Skip to content

Commit

Permalink
Merge pull request #38 from LegrandNico/master
Browse files Browse the repository at this point in the history
version 0.4.0
  • Loading branch information
micahgallen committed Apr 2, 2021
2 parents bc8b21f + 34d40b7 commit bd250eb
Show file tree
Hide file tree
Showing 30 changed files with 1,896 additions and 16,164 deletions.
Binary file modified .coverage
Binary file not shown.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.ipynb_checkpoints
*.pyc
.mypy_cache
.pytest_cache
.pytest_cache
/htmlcov
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ repos:
rev: v4.3.21
hooks:
- id: isort
exclude: tests/
- repo: https://github.com/ambv/black
rev: stable
hooks:
Expand Down
4 changes: 3 additions & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ include requirements.txt
include cardioception/HRD/Images/*
include cardioception/HRD/Sounds/*
include cardioception/HBC/Images/*
include cardioception/HBC/Sounds/*
include cardioception/HBC/Sounds/*
include cardioception/notebooks/data/HBC/*
include cardioception/notebooks/data/HRD/*
148 changes: 101 additions & 47 deletions README.md

Large diffs are not rendered by default.

Empty file.
Empty file.
114 changes: 72 additions & 42 deletions cardioception/HBC/parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,72 +29,85 @@ def getParameters(
----------
participant : str
Subject ID. Default is 'exteroStairCase'.
session : int
Session number. Default to '001'.
resultPath : str or None
Where to save the results.
screenNb : int
Screen number. Used to parametrize py:func:`psychopy.visual.Window`.
Default is set to 0.
serialPort: str
The USB port where the pulse oximeter is plugged. Should be written as
a string e.g., 'COM3', 'COM4'. If set to *None*, the pulse oximeter
will be automatically detected. using the
:py:func:`systole.recording.findOximeter()` function.
taskVersion : str or None
Task version to run. Can be 'Garfinkel', 'Shandry', 'test' or None.
resultPath : str or None
Where to save the results.
session : int
Session number. Default to '001'.
setup : str
Context of oximeter recording. Behavioral will record through a Nonin
pulse oximeter, *fMRI* will record through BrainVision amplifier
through TCP/IP conneciton. *test* will use pre-recorded pulse time
series (for testing only).
systole_kw : dict
Additional keyword arguments for :py:class:`systole.recorder.Oxmeter`.
taskVersion : str or None
Task version to run. Can be 'Garfinkel', 'Shandry', 'test' or None.
Attributes
----------
restPeriod : bool
If *True*, a resting period will be proposed before the task.
restLength : int
The length of the resting period (seconds). Default is 300 seconds.
screenNb : int
The screen number (Psychopy parameter). Default set to 0.
randomize : bool
If `True` (default), will randomize the order of the conditions. If
taskVersion is not None, will use the default task parameter instead.
startKey : str
The key to press to start the task and go to next steps.
rating : bool
If `True` (default), will add a rating scale after the evaluation.
conditions : 1d array-like of str
The conditions. Can be 'Rest', 'Training' or 'Count'.
confScale : list
The range of the confidence rating scale.
heartLogo : `psychopy.visual.ImageStim`
Image presented during resting conditions.
labelsRating : list
The labels of the confidence rating scale.
taskVersion : str or None
Task version to run. Can be 'Garfinkel', 'Shandry' or None.
times : 1d array-like of int
Length of trials, in seconds.
conditions : 1d array-like of str
The conditions. Can be 'Rest', 'Training' or 'Count'.
subjectID : str
Subject identifiant.
subjectNumber : int
Subject reference number.
noteStart : psychopy.sound.Sound instance
The sound that will be played when trial starts.
noteStop : psychopy.sound.Sound instance
The sound that will be played when trial ends.
path : str
The task working directory.
results : str
randomize : bool
If `True` (default), will randomize the order of the conditions. If
taskVersion is not None, will use the default task parameter instead.
rating : bool
If `True` (default), will add a rating scale after the evaluation.
restLength : int
The length of the resting period (seconds). Default is 300 seconds.
restLogo : `psychopy.visual.ImageStim`
Image presented during resting conditions.
restPeriod : bool
If *True*, a resting period will be proposed before the task.
resultPath : str
The subject result directory.
note : `psychopy.sound`
The sound played at trial start and trial end.
win : `psychopy.visual.Window`
Window where to present stimuli.
screenNb : int
The screen number (Psychopy parameter). Default set to 0.
serial : `serial.Serial`
The serial port used to record the PPG activity.
restLogo : `psychopy.visual.ImageStim`
Image presented during resting conditions.
heartLogo : `psychopy.visual.ImageStim`
Image presented during resting conditions.
startKey : str
The key to press to start the task and go to next steps.
taskVersion : str or None
Task version to run. Can be 'Garfinkel', 'Shandry', 'test' or None.
texts : dict
Dictionnary containing the texts to be presented.
textSize : float
Text size.
triggers : dict
Dictionary {str, callable or None}. The function will be executed
before the corresponding trial sequence. The default values are
`None` (no trigger sent).
* `"trialStart"`
* `"trialStop"`
* `"listeningStart"`
* `"listeningStop"`
* `"decisionStart"`
* `"decisionStop"`
* `"confidenceStart"`
* `"confidenceStop"`
times : 1d array-like of int
Length of trials, in seconds.
win : `psychopy.visual.window`
The window in which to draw objects.
"""
parameters: Dict[str, Any] = {}
parameters["restPeriod"] = True
Expand All @@ -106,6 +119,21 @@ def getParameters(
parameters["labelsRating"] = ["Guess", "Certain"]
parameters["taskVersion"] = taskVersion
parameters["results_df"] = pd.DataFrame({})
parameters["setup"] = setup

# Initialize triggers dictionary with None
# Some or all can later be overwrited with callable
# sending the information needed.
parameters["triggers"] = {
"trialStart": None,
"trialStop": None,
"listeningStart": None,
"listeningStop": None,
"decisionStart": None,
"decisionStop": None,
"confidenceStart": None,
"confidenceStop": None,
}

# Experimental design - can choose between a version based on recent
# papers from Sarah Garfinkel's group, or the classic Schandry approach.
Expand Down Expand Up @@ -149,30 +177,32 @@ def getParameters(
parameters["noteStart"] = sound.Sound(
pkg_resources.resource_filename("cardioception.HBC", "Sounds/start.wav")
)
parameters["noteEnd"] = sound.Sound(

parameters["noteStop"] = sound.Sound(
pkg_resources.resource_filename("cardioception.HBC", "Sounds/stop.wav")
)

# Open window
if parameters["setup"] == "test":
fullscr = False
parameters["win"] = visual.Window(screen=screenNb, fullscr=fullscr, units="height")
parameters["win"].mouseVisible = False

parameters["restLogo"] = visual.ImageStim(
win=parameters["win"],
units="height",
image=os.path.dirname(__file__) + "/Images/rest.png",
image=pkg_resources.resource_filename(__name__, "Images/rest.png"),
pos=(0.0, -0.2),
)
parameters["restLogo"].size *= 0.15
parameters["heartLogo"] = visual.ImageStim(
win=parameters["win"],
units="height",
image=os.path.dirname(__file__) + "/Images/heartbeat.png",
image=pkg_resources.resource_filename(__name__, "Images/heartbeat.png"),
pos=(0.0, -0.2),
)
parameters["heartLogo"].size *= 0.05

parameters["setup"] = setup
if setup == "behavioral":
# PPG recording
if serialPort is None:
Expand Down
47 changes: 29 additions & 18 deletions cardioception/HBC/task.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@

import numpy as np
import pandas as pd
from psychopy import core, event, visual
from psychopy import core, event, sound, visual


def run(
parameters: dict,
confidenceRating: bool = True,
runTutorial: bool = True,
win: Optional[visual.Window] = None,
):
Expand All @@ -19,13 +18,11 @@ def run(
----------
parameters : dict
Task parameters.
confidenceRating : bool
Whether the trial show include a confidence rating scale.
tutorial : bool
If *True*, will present a tutorial with 10 training trial with feedback
If `True`, will present a tutorial with 10 training trial with feedback
and 5 trials with confidence rating.
win : `psychopy.visual.Window`
Window where to present stimuli.
win : `psychopy.visual.window` or None
The window in which to draw objects.
"""
if win is None:
win = parameters["win"]
Expand All @@ -44,10 +41,14 @@ def run(
range(0, len(parameters["conditions"])),
):

parameters["triggers"]["trialStart"] # Send trigger or None

nCount, confidence, confidenceRT = trial(
condition, duration, nTrial, parameters, win
)

parameters["triggers"]["trialStop"] # Send trigger or None

# Store results in a DataFrame
parameters["results_df"] = parameters["results_df"].append(
pd.DataFrame(
Expand All @@ -65,7 +66,7 @@ def run(

# Save the results at each iteration
parameters["results_df"].to_csv(
parameters["results"]
parameters["resultPath"]
+ "/"
+ parameters["participant"]
+ parameters["session"]
Expand All @@ -75,7 +76,7 @@ def run(

# Save results
parameters["results_df"].to_csv(
parameters["results"]
parameters["resultPath"]
+ "/"
+ parameters["participant"]
+ parameters["session"]
Expand Down Expand Up @@ -114,8 +115,8 @@ def trial(
Trial number.
parameters : dict
Task parameters.
win : `psychopy.visual.Window`
Window where to present stimuli.
win : `psychopy.visual.window` or None
The window in which to draw objects.
Returns
-------
Expand Down Expand Up @@ -177,6 +178,7 @@ def trial(
# Add event marker
parameters["oxiTask"].channels["Channel_0"][-1] = 1
parameters["noteStart"].play()
parameters["triggers"]["listeningStart"]
core.wait(1)

# Record for a desired time length
Expand All @@ -187,7 +189,8 @@ def trial(
# Add event marker
parameters["oxiTask"].readInWaiting()
parameters["oxiTask"].channels["Channel_0"][-1] = 2
parameters["noteEnd"].play()
parameters["noteStop"].play()
parameters["triggers"]["listeningStop"]
core.wait(3)
parameters["oxiTask"].readInWaiting()

Expand All @@ -196,7 +199,7 @@ def trial(

# Save recording
parameters["oxiTask"].save(
parameters["results"]
parameters["resultPath"]
+ "/"
+ parameters["participant"]
+ str(nTrial)
Expand All @@ -218,6 +221,8 @@ def trial(
messageCount.draw()
win.flip()

parameters["triggers"]["decisionStart"] # Send trigger or None

nCounts = ""
while True:

Expand Down Expand Up @@ -295,6 +300,8 @@ def trial(
messageCount.draw()
win.flip()

parameters["triggers"]["decisionStop"] # Send trigger or None

##############
# Rating scale
##############
Expand All @@ -316,12 +323,14 @@ def trial(
text=parameters["texts"]["confidence"],
height=parameters["textSize"],
)
parameters["triggers"]["confidenceStart"]
while ratingScale.noResponse:
message.draw()
ratingScale.draw()
win.flip()
confidence = ratingScale.getRating()
confidenceRT = ratingScale.getRT()
parameters["triggers"]["confidenceStop"]

finalCount = int(nCounts) if nCounts else None

Expand All @@ -335,8 +344,8 @@ def tutorial(parameters: dict, win: Optional[visual.Window] = None):
----------
parameters : dict
Task parameters.
win : `psychopy.visual.Window`
Window where to present stimuli.
win : `psychopy.visual.window` or None
The window in which to draw objects.
"""
if win is None:
win = parameters["win"]
Expand Down Expand Up @@ -500,8 +509,10 @@ def rest(
----------
parameters : dict
Task parameters.
win : `psychopy.visual.Window`
Window where to present stimuli.
duration : float
Duration or the recording (seconds).
win : `psychopy.visual.window` or None
The window in which to draw objects.
"""
if win is None:
win = parameters["win"]
Expand All @@ -523,5 +534,5 @@ def rest(

# Save recording
parameters["oxiTask"].save(
parameters["results"] + "/" + parameters["participant"] + "_Rest"
parameters["resultPath"] + "/" + parameters["participant"] + "_Rest"
)
Loading

0 comments on commit bd250eb

Please sign in to comment.