fast-feedback virtual target task code on STM Nucleo

Dependencies:   mbed

trial.cpp

Committer:
gwappa
Date:
2018-07-22
Revision:
28:797536a42b9f
Parent:
27:b31ea8d74f9e
Child:
29:1fb060aab1f8

File content as of revision 28:797536a42b9f:

#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;
    reset       = false;
}

void TrialFlag::writeToSerial() {
    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){
    // 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_dur_us      = 0;
    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 Condition:
        visualOut.setBlinkDuration( calc_blink_duration(task.vis_blink_hz.value) );
        assignCues(task, true, false);
        break;
    case Report:
    case Associate:
        visualOut.setBlinkDuration( calc_blink_duration(task.vis_blink_hz.value) );
        assignCues(task, true);
        break;
    case Motion:
    case MotionAlt:
        assignCues(task, false);
        break;
    }
    
    // assign Auditory cues (if any)
    switch (task.mode.value) {
    case Condition:
    case Report:
        break;
    case Associate:
    case Motion:
    case MotionAlt:
        audioOut.setEnabled(true);
        audioOut.setOnset(0);
        audioOut.setDuration(cued_dur_us);
        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);
}

#include "IO.h"

void Trial::assignCues(const Task& task, const bool& assignVisual, const bool& canfail)
{
    if (!assignVisual) {
        visualOut.setEnabled(false);
        visualOut.direct(true);
        vis_dur_us      = 0;
        vis_onset_us    = 0;
        
        cued_dur_us     = ms_to_us(task.aud_dur_ms.value);
        return;
    }
    
    const bool _canfail = canfail && (task.vis_fail_perc.value > 0);
    
    const uint32_t  phasedur_us = ms_to_us(task.aud_dur_ms.value);
    const uint32_t  minonset_us = ms_to_us(task.pre_min_ms.value);
    const uint32_t  respdur_us  = ms_to_us(task.resp_dur_ms.value);
    const uint32_t  avgdur_us   = ms_to_us(task.vis_avg_ms.value);
    const uint32_t  mindur_us   = ms_to_us(task.vis_min_ms.value);
    const double    failure_perc = _canfail? ((double)(task.vis_fail_perc.value)):1e-6;
    
    const double    onset_vrng  = (double)(phasedur_us - minonset_us - respdur_us - mindur_us);
    const double    logdecay    = ::log(100.0) - ::log(failure_perc);
    const double    ftau        = onset_vrng/logdecay;
    const uint32_t  tau         = (uint32_t)(ftau + 0.5);
    const uint32_t  onset_us    = random::exponential(tau, onset_vrng+10) + minonset_us;
    const uint32_t  maxonset_us = onset_vrng + minonset_us;
    
    if ((onset_us > 0) && (onset_us < maxonset_us)) {
        // generate duration
        const uint32_t dur_vrng = maxonset_us - onset_us;
        const uint32_t dur_us   = random::exponential(avgdur_us - mindur_us, dur_vrng) + mindur_us;
        
        visualOut.setEnabled(true);
        visualOut.setDuration(dur_us);
        visualOut.setOnset(onset_us);
        
        vis_dur_us      = dur_us;
        vis_onset_us    = onset_us;
        
        cued_dur_us     = onset_us + dur_us + respdur_us;
        
    } else {
        // no pulse
        visualOut.setEnabled(false);
        vis_dur_us      = 0;
        vis_onset_us    = 0;
        
        cued_dur_us     = phasedur_us;
    }
}

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

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

void Trial::markTrialEnd() {
    trialEnd.run();
    trialEnd.wait();
    writeToSerial();
    TrialTimer.stop();
    TrialTimer.reset();
    visualOut.direct(false); // if any
}

void Trial::writeToSerial() {
    IO::write("%c",IO::RESULT_HEADER);
    flag.writeToSerial();
    
    IO::write(";wait%u", waiting);
    
    if (visualOut.isEnabled() && (!flag.reset)) {
        IO::write(";visual(onset%s,duration%s)",
            uint64_to_str(vis_onset_us).c_str(),
            uint64_to_str(vis_dur_us).c_str());
    }
    
    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");
}