fast-feedback virtual target task code on STM Nucleo

Dependencies:   mbed

trial.cpp

Committer:
gwappa
Date:
2018-07-02
Revision:
20:4c06d3041337
Parent:
19:50663f8815b8
Child:
22:41163fb3fdc6

File content as of revision 20:4c06d3041337:

#include "trial.h"
#include "rig.h"
#include "IO.h"
#include "random.h"
#include "events.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);
                    
    // generate stimuli
    audioOut.setEnabled(false);
    visualOut.setEnabled(false);
    vis_dur_us      = 0;
    vis_onset_us    = 0;
    
    switch (task.mode.value) {
    case Pair:
        visualOut.setBlinkDuration( calc_blink_duration(task.vis_blink_hz.value) );
        assignRandomStim(task);
        rewardOut.setDuration(ms_to_us(task.reward_dur_ms.value));
        break;
    case Report:
    case Associate:
        assignRandomStim(task);
        // fallthrough
    case Motion:
    case MotionAlt:
        rewardOut.setOnset(0);
        rewardOut.setDuration(ms_to_us(task.reward_dur_ms.value));
        if (task.mode.value != Report) {
            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
    licking_events.clear();
    whisking_events.clear();
}

void Trial::assignRandomStim(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.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 = (task.vis_fail_perc.value > 0)? ((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;
        IO::debug("visual(onset=%u,dur=%u)",vis_onset_us,vis_dur_us);
    } else {
        // no pulse
        visualOut.setEnabled(false);
        vis_dur_us      = 0;
        vis_onset_us    = 0;
        IO::debug("visual(disabled)");
    }
}

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

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

void Trial::markTrialEnd() {
    trialEnd.run();
    trialEnd.wait();
    writeToSerial();
    timer.stop();
    timer.reset();
}

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%u,duration%u)",vis_onset_us,vis_dur_us);
    }
    
    trialtime_t zero = (flag.reset)? starting : cuestarting;
    IO::write(";whisk");
    whisking_events.writeToSerial(zero);
    IO::write(";lick");
    licking_events.writeToSerial(zero);
    IO::write(";\r\n");
}