fast-feedback virtual target task code on STM Nucleo

Dependencies:   mbed

states.cpp

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

File content as of revision 32:1416e015016c:

#include "states.h"
#include "rig.h"
#include "automaton.h"
#include "scheduler.h"
#include "IO.h"

#define STATE_IS_LOGGED

#ifdef STATE_IS_LOGGED
#define LOGSTATE(S) IO::info(#S);
#else
#define LOGSTATE(S)
#endif

void finalize() {
    trial.markTrialEnd(task);
    scheduler::reset();
}

void Delay::setup() {
    LOGSTATE(Delay)
    
    // initialize the trial-related params
    trial.reset(task);
    
    // set up the timeout for the next state
    switch (task.mode.value) {
    case MotionAlt:
        scheduler::set(ms_to_us(trial.delay_dur_ms + task.prep_dur_ms.value), &automaton::jump<Delay,Staged>);
        break;
    default:
        if (task.prep_dur_ms.value > 0) {
            scheduler::set(ms_to_us(trial.delay_dur_ms), &automaton::jump<Delay,Prepare>);
        } else {
            switch (task.mode.value) {
            case Pair:
                scheduler::set(ms_to_us(trial.delay_dur_ms), &automaton::jump<Delay,Paired>);
                break;
            case Report:
                scheduler::set(ms_to_us(trial.delay_dur_ms), &automaton::jump<Delay,Cued>);
                break;
            case Stage:
            case Associate:
            case Motion:
                scheduler::set(ms_to_us(trial.delay_dur_ms), &automaton::jump<Delay,Staged>);
            }
        }
    }
    
    trial.markTrialStart();
}

void Delay::teardown() {
    // do nothing
}

void Prepare::setup() {
    // mostly the same with for Delay
    // except that the animal cannot lick freely
    
    LOGSTATE(Prepare)
    
    // configure the interrupts
    lickIn.attach(&automaton::jump<Prepare,Abort>);
    
    // set timeout for the next state
    switch (task.mode.value) {
    case Pair:
        scheduler::set(ms_to_us(task.prep_dur_ms.value), &automaton::jump<Prepare,Paired>);
        break;
    case Report:
        scheduler::set(ms_to_us(task.prep_dur_ms.value), &automaton::jump<Prepare,Cued>);
        break;
    case Stage:
    case Associate:
    case Motion:
        scheduler::set(ms_to_us(task.prep_dur_ms.value), &automaton::jump<Delay,Staged>);
    }
}

void Prepare::teardown() {
    // de-register lick inhibition
    lickIn.detach();
}

void Staged::setup() {
    LOGSTATE(Staged)
    
    trial.markEndOfWait();
    audioOut.attachTurnOffCallback(&automaton::jump<Staged,NoResp>);
    audioOut.run();
    
    switch (task.mode.value) {
    case Stage:
        // no punishment in response to lick
        if (visualOut.isEnabled()) {
            lickIn.detach(); // if any
            // silently goes to the paired state
            visualOut.attachTurnOnCallback(&automaton::jump<Staged,Paired>);
        } else {
            lickIn.attach(&Staged::logCatch);
        }
        break;
    case MotionAlt:
        // no punishment in response to lick
        // silently goes to the rewarded state
        trial.flag.cued = true;
        gateIn.attach(&automaton::jump<Staged,WithResp>);
        break;
    case Associate:
        lickIn.attach(&automaton::jump<Staged,WithResp>); // catch trial
        visualOut.attachTurnOnCallback(&automaton::jump<Staged,Cued>);
        break;
    case Motion:
        lickIn.attach(&automaton::jump<Staged,WithResp>); // catch trial
        gateIn.attach(&automaton::jump<Staged,Cued>);
    }
    
    switch (task.mode.value) {
    case Motion:
    case MotionAlt:
        break;
    case Stage:
    case Associate:
    default:
        visualOut.run(); // should run with an appropriate delay
                         // and activates gateIn later
    }
}

void Staged::logCatch() {
    trial.flag.responded = true;
}

void Staged::teardown() {
    gateIn.detach();
    lickIn.detach(); // if any
    audioOut.detachTurnOffCallback(); // in case the next state fails to do so
}

void Paired::setup() {
    LOGSTATE(Paired)
    
    if (task.mode.value == Pair) {
        trial.markEndOfWait();
        visualOut.run();
    }
    trial.flag.cued = true;
    visualOut.attachTurnOffCallback(&automaton::jump<Paired,Monitored>);
    lickIn.attach(&automaton::jump<Paired,Hit>);
}

void Paired::teardown() {
    visualOut.detachTurnOffCallback(); // if any
    lickIn.detach(); // if any
}

void Hit::setup() {
    LOGSTATE(Hit)
    trial.flag.rewarded = false; // force "test" trial mode
    scheduler::set(ms_to_us(1), &automaton::jump<Hit,WithResp>);
}

void Hit::teardown() {
    // do nothing
}

void Cued::setup() {
    LOGSTATE(Cued)
    
    if (task.mode.value == Report) {
        trial.markEndOfWait();
        scheduler::set(ms_to_us(task.vis_dur_ms.value + task.resp_dur_ms.value), &automaton::jump<Cued,NoResp>);
    }
    scheduler::set(ms_to_us(task.vis_dur_ms.value + task.resp_dur_ms.value), &automaton::jump<Cued,NoResp>);
    trial.flag.cued = true;
    
    lickIn.attach(&automaton::jump<Cued,WithResp>);
}

void Cued::teardown() {
    lickIn.detach();
    
    // end cue output
    switch (task.mode.value) {
    case Report:
    case Associate:
        visualOut.stop();
    }
    
    switch (task.mode.value) {
    case Stage:
    case Associate:
    case Motion:
    case MotionAlt:
        audioOut.stop();
    }
}

void Abort::setup() {
    LOGSTATE(Abort)
    
    audioOut.stop(); // if any
    visualOut.stop(); // if any
    trial.markEndOfWait();
    trial.flag.reset     = true;
    scheduler::set(ms_to_us(task.post_dur_ms.value), &automaton::done<Abort>);
}

void Abort::teardown() {
    finalize();
}

void Monitored::setup() {
    LOGSTATE(Monitored)
    
    if (trial.flag.rewarded == true) {
        rewardOut.run();
    }
    lickIn.attach(&automaton::jump<Monitored,WithResp>);
    scheduler::set(ms_to_us(task.resp_dur_ms.value), &automaton::jump<Monitored,NoResp>);
}

void Monitored::teardown() {
    audioOut.stop(); // if any
    lickIn.detach();
    rewardOut.wait();
}

void WithResp::setup() {
    LOGSTATE(WithResp)
    
    trial.flag.responded = true;
    
    audioOut.stop(); // if any (for Stage/MotionAlt)
    visualOut.stop(); // if any
    
    switch (task.mode.value) {
    case Pair:
    case Stage:
        if (!trial.flag.rewarded) {
            rewardOut.start();
        }
        break;
    case Report:
    case Associate:
    case Motion:
    case MotionAlt:
        if (trial.flag.cued) {
            rewardOut.start();
        }
    }
    
    scheduler::set(ms_to_us(task.post_dur_ms.value), &automaton::done<WithResp>);
}

void WithResp::teardown() {
    finalize();
}

void NoResp::setup() {
    LOGSTATE(NoResp)
        
    scheduler::set(ms_to_us(task.post_dur_ms.value), &automaton::done<NoResp>);
}
void NoResp::teardown() {
    finalize();
}

void TestReward::setup() {
    LOGSTATE(TestReward)
    
    // open/close the valve
    rewardOut.setOnset(0);
    rewardOut.setDuration(ms_to_us(task.reward_dur_ms.value));
    rewardOut.start();
    
    scheduler::set(ms_to_us(task.reward_dur_ms.value+100), &automaton::done<TestReward>);
}

void TestReward::teardown() {
    // do nothing
}

void ClearTrialIndex::setup() {
    trial.index = 1;
    scheduler::set(ms_to_us(10), &automaton::done<ClearTrialIndex>);
}

void ClearTrialIndex::teardown() {
    // do nothing
}