fast-feedback virtual target task code on STM Nucleo

Dependencies:   mbed

trial.cpp

Committer:
gwappa
Date:
2018-12-13
Revision:
32:1416e015016c
Parent:
31:b320ca61a8c0

File content as of revision 32:1416e015016c:

#include "trial.h"
#include "rig.h"
#include "IO.h"
#include "random.h"

const uint64_t MicrosecondsInSecond = 1000000;

inline uint64_t calc_blink_duration(const uint16_t& freq) {
    return (freq == 0)? 0 : (MicrosecondsInSecond/(freq * 2));
}

void TrialFlag::clear() {
    cued        = false;
    responded   = false;
    rewarded    = false;
    reset       = false;
}

void TrialFlag::writeToSerial(const Task& task) {
    if (task.mode.value == Pair)
    {
        if (reset) {
            IO::write("reset");
        } else if (rewarded) {
            IO::write(responded? "resp":"noresp");
        } else {
            IO::write(responded? "hit":"miss");
        }
    }
    else
    {
        if (reset) {
            IO::write("reset");
        } else if (cued) {
            IO::write(responded? "hit":"miss");
        } else {
            IO::write(responded? "catch":"reject");
        }
    }
}

void Trial::reset(const Task& task){
    
    if (task.vis_test_every.value > 0) {
        index = index % task.vis_test_every.value;
    } else {
        if (index == 101) {
            index = 1;
        }
    }
    
    // reset score
    flag.clear();
    
    // set delay duration
    delay_dur_ms  = task.delay_min_ms.value
                    + random::exponential(task.delay_var_ms.value, task.delay_var_ms.value*3);
                    
    // reset cues
    audioOut.setEnabled(false);
    visualOut.setEnabled(false);
    vis_onset_us    = 0;
    
    // configure reward
    rewardOut.setOnset(0);
    rewardOut.setDuration(ms_to_us(task.reward_dur_ms.value));
    
    // configure cued state duration + visual stimulus position (if any)
    switch (task.mode.value) {
    case Pair:
        flag.rewarded = (index > 0);
        visualOut.setEnabled(true);
        visualOut.setOnset(0);
        visualOut.setDuration(ms_to_us(task.vis_dur_ms.value));
        break;
    case Report:
        visualOut.setEnabled(index > 0);
        visualOut.setOnset(0);
        visualOut.setDuration(ms_to_us(task.vis_dur_ms.value));
        break;
    case Stage:
    case Associate:
        assignAssociative(task);
        break;
    case Motion:
    case MotionAlt:
        assignForFeedback(task);
        break;
    }
    
    // assign Auditory cues (if any)
    switch (task.mode.value) {
    case Pair:
    case Report:
        break;
    case Stage:
    case Associate:
    case Motion:
    case MotionAlt:
        audioOut.setEnabled(true);
        audioOut.setOnset(0);
        audioOut.setDuration(ms_to_us(task.aud_dur_ms.value));
        audioOut.setBlinkDuration( calc_blink_duration(task.aud_tick_hz.value) );
        break;
    }
    
    // reset interrupt events
    lickIn.reset(task.lick_debounce_ms.value, true);
    whiskIn.reset(task.whisk_debounce_ms.value, true);
    gateIn.reset(-1, true);
}

void Trial::assignForFeedback(const Task& task)
{
    visualOut.setEnabled(false);
    visualOut.direct(true);
    vis_onset_us    = 0;
}

void Trial::assignAssociative(const Task& task)
{   
    const uint32_t  phasedur_us = ms_to_us(task.aud_dur_ms.value);
    const uint32_t  minonset_us = ms_to_us(task.onset_min_ms.value);
    const uint32_t  respdur_us  = ms_to_us(task.resp_dur_ms.value);
    const uint32_t  visdur_us   = ms_to_us(task.vis_dur_ms.value);
    
    const double    onset_vrng  = (double)(phasedur_us - minonset_us - respdur_us - visdur_us);
    const uint16_t  nsteps      = task.onset_steps_n.value;
    const uint32_t  onset_step  = random::unif(nsteps);
    
    vis_onset_us = (uint32_t)((onset_vrng*onset_step/nsteps) + minonset_us);
    
    visualOut.setEnabled(index > 0);
    visualOut.setOnset(vis_onset_us);
    visualOut.setDuration(ms_to_us(task.vis_dur_ms.value));
}

void Trial::markTrialStart() {
    starting = TrialTimer.read_ms();
    trialStart.run();
    TrialTimer.start();
}

void Trial::markEndOfWait() {
    cuestarting = TrialTimer.read_ms();
    waiting     = cuestarting - starting;
}

void Trial::markTrialEnd(const Task& task) {
    trialEnd.run();
    trialEnd.wait();
    writeToSerial(task);
    TrialTimer.stop();
    TrialTimer.reset();
    visualOut.direct(false); // if any
    
    if (!flag.reset) {
        index++;
    }
}

void Trial::writeToSerial(const Task& task) {
    IO::write("%c",IO::RESULT_HEADER);
    flag.writeToSerial(task);
    
    IO::write(";index%u;wait%u", index, waiting);
    
    switch (task.mode.value) {
    case Stage:
    case Associate:
        if ((visualOut.isEnabled()) && (!flag.reset)) {
            IO::write(";visual%u",(uint16_t)(vis_onset_us/1000));
        }
    }
    
    trialtime_t zero = (flag.reset)? starting : cuestarting;
    IO::write(";whisk");
    whiskIn.writeToSerial(zero);
    IO::write(";gate");
    gateIn.writeToSerial(zero);
    IO::write(";lick");
    lickIn.writeToSerial(zero);
    IO::write(";\r\n");
}