BeNeuroLab / PyTreadmillTask

Spherical treadmill task based on the PyControl framework
1 stars 4 forks source link

task2: increase motion threshold in penalty and reward #47

Closed AtMostafa closed 1 year ago

AtMostafa commented 1 year ago

no need for an event

AtMostafa commented 1 year ago

working task2:

# PyTreadmillTask

from pyControl.utility import *
import hardware_definition as hw
from devices import *
import math

# -------------------------------------------------------------------------
# States and events.
# -------------------------------------------------------------------------

states = ['trial',
          'disengaged',
          'led_on',
          'reward',
          'penalty']

events = ['motion',
          'lick',
          'session_timer'
          ]

initial_state = 'trial'

# -------------------------------------------------------------------------
# Variables.
# -------------------------------------------------------------------------

# general parameters
v.target_angle___ = {0: math.pi / 4,
                     1: math.pi / 4,
                     2: math.pi / 2,
                     3: 3 * math.pi / 4,
                     4: 3 * math.pi / 4}
v.led_cues___ = list(v.target_angle___.keys())

v.audio_f_range___ = (10000, 20000)  # between 10kHz and 20kHz, loosely based on Heffner & Heffner 2007

# session params
v.session_duration = 1 * hour
v.reward_duration = 30 * ms
v.trial_number = 0
v.reward_number = 0

# intertrial params
v.min_IT_movement = 10  # cm - must be a multiple of 5
v.min_IT_duration = 3 * second
v.max_IT_duration = 15 * second
v.n_lick___ = 5
v.n_motion___ = 0
v.x___ = 0
v.y___ = 0

# trial params
v.max_led_duration = 10 * second
v.distance_to_target = 20  # cm - must be a multiple of 5
v.target_angle_tolerance = math.pi / 4  # deg_rad
v.led_direction = -1

# -------------------------------------------------------------------------
# State-independent Code
# -------------------------------------------------------------------------

def cue_left_right(LedDevice: LEDStim):
    """
    Cues the right leds or the left leds, randomly.
    centre led always cued
    """
    LedDevice.all_off()
    if random() >= 0.5:
        cues = v.led_cues___[:3]
        out = 1
    else:
        cues = v.led_cues___[-3:]
        out = 3

    print('{}, LED_direction'.format(cues))
    LedDevice.cue_array(cues)

    return out

def arrived_to_target(dX: float, dY: float,
                      stim_direction: int,
                      target_angle_tolerance: float):
    """
    checks the motion direction against the target direction
    MUST have 5 stim directions
    """
    assert stim_direction < 5, 'wrong direction value'

    move_angle = math.atan2(dX, dY)  # straight is pi/2
    print('{}, run_angle_rad'.format(move_angle))
    if abs(move_angle - v.target_angle___[stim_direction]) < target_angle_tolerance:
        return True
    else:
        return False

def audio_mapping(d_a: float) -> float:
    """
    freq = (-10kHz/300)d_a + 15kHz
    """
    return mean(v.audio_f_range___) - (v.audio_f_range___[0] * d_a / v.target_angle___[0] * 2)

def audio_feedback(speaker,
                   dX: float, dY: float,
                   stim_direction: int):
    """ Set the audio frequency based on the direction of the movement. """
    angle = math.atan2(dY, dX)
    audio_freq = audio_mapping(angle - v.target_angle___[stim_direction])
    speaker.sine(audio_freq)

# -------------------------------------------------------------------------
# Define behaviour.
# -------------------------------------------------------------------------

# Run start and stop behaviour.
def run_start():
    "Code here is executed when the framework starts running."
    set_timer('session_timer', v.session_duration, True)
    hw.motionSensor.record()
    hw.speaker.set_volume(60)
    hw.speaker.off()
    hw.LED_Delivery.all_off()
    print('{}, CPI'.format(hw.motionSensor.sensor_x.CPI))
    hw.reward.reward_duration = v.reward_duration

def run_end():
    """ 
    Code here is executed when the framework stops running.
    Turn off all hardware outputs.
    """
    hw.LED_Delivery.all_off()
    hw.reward.stop()
    hw.speaker.off()
    hw.motionSensor.off()
    hw.off()

# State behaviour functions.

def trial(event):
    "beginning of the trial"
    if event == 'entry':
        hw.speaker.noise(20000)
        v.trial_number += 1
        print('{}, trial_number'.format(v.trial_number))
        hw.LED_Delivery.all_off()
        timed_goto_state('disengaged', v.max_IT_duration)
    elif event == 'motion' or event == 'lick':  # any action will start the trial
        goto_state('led_on')

def led_on(event):
    "stimulation onset"
    if event == 'entry':
        timed_goto_state('disengaged', v.max_led_duration)
        hw.speaker.noise(20000)
        v.led_direction = cue_left_right(hw.LED_Delivery)
        v.n_motion___ = 0
        hw.motionSensor.threshold = 5
    elif event == 'motion':
        if v.n_motion___ * 5 < v.distance_to_target:
            arrived = arrived_to_target(v.x___, v.y___,
                                        v.led_direction,
                                        v.target_angle_tolerance)

            #audio_feedback(hw.speaker, v.x___, v.y___, v.led_direction)

            if arrived is True:
                goto_state('reward')
        else:
            goto_state('penalty')

def reward(event):
    "reward state"
    if event == 'entry':
        hw.LED_Delivery.all_off()
        hw.speaker.off()
        if v.n_lick___ >= 3:
            hw.reward.release()
            v.n_lick___ = 0
            v.reward_number += 1
            print('{}, reward_number'.format(v.reward_number))
        timed_goto_state('trial', randint(v.min_IT_duration, v.max_led_duration))

def penalty(event):
    "penalty state"
    if event == 'entry':
        hw.LED_Delivery.all_off()
        hw.speaker.clicks(5)
        timed_goto_state('trial', v.max_IT_duration)

def disengaged(event):
    "disengaged state"
    if event == 'entry':
        hw.LED_Delivery.all_off()
        hw.speaker.off()
    elif event =='motion' or event == 'lick':
        goto_state('led_on')

# State independent behaviour.
def all_states(event):
    """
    Code here will be executed when any event occurs,
    irrespective of the state the machine is in.
    """
    if event == 'motion':
        # read the motion registers
        # to convert to cm, divide by CPI and multiply by 2.54
        v.x___ = hw.motionSensor.x / hw.motionSensor.sensor_x.CPI * 2.54
        v.y___ = hw.motionSensor.y / hw.motionSensor.sensor_x.CPI * 2.54
        # print('{},{}, dM'.format(v.x___, v.y___))
        v.n_motion___ += 1
    elif event == 'lick':
        v.n_lick___ += 1
    elif event == 'session_timer':
        hw.motionSensor.stop()
        stop_framework()