Keisuke Sehara
/
STM32_Whisking
fast-feedback virtual target task code on STM Nucleo
Revision 32:1416e015016c, committed 2018-12-13
- Comitter:
- gwappa
- Date:
- Thu Dec 13 07:18:43 2018 +0000
- Parent:
- 31:b320ca61a8c0
- Commit message:
- change to use the Staged state
Changed in this revision
--- a/states.cpp Sun Oct 14 14:45:02 2018 +0000 +++ b/states.cpp Thu Dec 13 07:18:43 2018 +0000 @@ -26,16 +26,23 @@ // 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,Cued>); + 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 { - if (task.mode.value == Condition) { + switch (task.mode.value) { + case Pair: scheduler::set(ms_to_us(trial.delay_dur_ms), &automaton::jump<Delay,Paired>); - } else { + 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>); } } } @@ -57,8 +64,18 @@ lickIn.attach(&automaton::jump<Prepare,Abort>); // set timeout for the next state - scheduler::set(ms_to_us(task.prep_dur_ms.value), - (task.mode.value == Condition)? &automaton::jump<Prepare,Paired> : &automaton::jump<Prepare,Cued>); + 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() { @@ -66,13 +83,70 @@ 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) - trial.markEndOfWait(); - visualOut.run(); + if (task.mode.value == Pair) { + trial.markEndOfWait(); + visualOut.run(); + } trial.flag.cued = true; - visualOut.attachTurnOffCallback(&automaton::jump<Paired,Monitor>); + visualOut.attachTurnOffCallback(&automaton::jump<Paired,Monitored>); lickIn.attach(&automaton::jump<Paired,Hit>); } @@ -91,67 +165,21 @@ // do nothing } -void Monitor::setup() { - LOGSTATE(Monitor) - - if (trial.flag.rewarded == true) { - rewardOut.run(); - } - lickIn.attach(&automaton::jump<Monitor,WithResp>); - scheduler::set(ms_to_us(task.resp_dur_ms.value), &automaton::jump<Monitor,NoResp>); -} - -void Monitor::teardown() { - lickIn.detach(); - rewardOut.wait(); -} - void Cued::setup() { LOGSTATE(Cued) - trial.markEndOfWait(); - - switch (task.mode.value) { - case Report: - case Associate: - case Motion: - // wait for the visual cue to flag "cued" - // use visual feedback trigger as the "gate" response - // licking without a visual "go-cue" will be considered a "catch" response - gateIn.attach(&Cued::gate); - lickIn.attach(&automaton::jump<Cued,WithResp>); - break; - case MotionAlt: - // jumps directly to WithResp with the visual cue - Cued::gate(); - gateIn.attach(&automaton::jump<Cued,WithResp>); - break; + 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; - // start cue output - visualOut.run(); - audioOut.run(); - - // sets the timeout for the next state - switch (task.mode.value) { - case Report: - scheduler::set(ms_to_us(task.vis_dur_ms.value + task.resp_dur_ms.value), &automaton::jump<Cued,NoResp>); - break; - default: - scheduler::set(ms_to_us(task.aud_dur_ms.value), &automaton::jump<Cued,NoResp>); - break; - } -} - -void Cued::gate() { - gateIn.detach(); lickIn.attach(&automaton::jump<Cued,WithResp>); - trial.flag.cued = true; // in case it has not been (i.e. other than MotionAlt) } void Cued::teardown() { lickIn.detach(); - gateIn.detach(); // end cue output switch (task.mode.value) { @@ -161,6 +189,7 @@ } switch (task.mode.value) { + case Stage: case Associate: case Motion: case MotionAlt: @@ -170,8 +199,9 @@ void Abort::setup() { LOGSTATE(Abort) - lickIn.detach(); // if any - gateIn.detach(); // if any + + 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>); @@ -181,19 +211,44 @@ 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; - lickIn.detach(); // if any - gateIn.detach(); // if any + + audioOut.stop(); // if any (for Stage/MotionAlt) + visualOut.stop(); // if any - if (task.mode.value == Condition) { + switch (task.mode.value) { + case Pair: + case Stage: if (!trial.flag.rewarded) { rewardOut.start(); } - } else if (trial.flag.cued) { - 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>); @@ -205,9 +260,7 @@ void NoResp::setup() { LOGSTATE(NoResp) - lickIn.detach(); // if any - gateIn.detach(); // if any - + scheduler::set(ms_to_us(task.post_dur_ms.value), &automaton::done<NoResp>); } void NoResp::teardown() {
--- a/states.h Sun Oct 14 14:45:02 2018 +0000 +++ b/states.h Thu Dec 13 07:18:43 2018 +0000 @@ -8,23 +8,7 @@ static void teardown(); }; -struct Paired { - static void setup(); - static void teardown(); -}; - -struct Hit { - static void setup(); - static void teardown(); -}; - -struct Monitor { - static void setup(); - static void teardown(); -}; - /** -* used except for the Condition mode. * lick is not allowed during this period. * if the animal licks, the state transits to Abort. */ @@ -33,12 +17,54 @@ static void teardown(); }; +/** +* comes to this state in response to a too early licking +* (e.g. during Prepare period). +* goes to the end of the trial without a reward delivery. +*/ struct Abort { static void setup(); static void teardown(); }; /** +* used during the Stage/Associate/Motion mode. +* the auditory cue is delivered here. +*/ +struct Staged { + static void setup(); + static void logCatch(); + static void teardown(); +}; + +/** +* used only for the Pair mode. +* except for during the 'testing' trial, reward is delivered unconditionally. +*/ +struct Paired { + static void setup(); + static void teardown(); +}; + +/** +* comes to this state during the Pair mode if the mouse licks before the +* end of the visual stimulus. +* the reward is delivered in response. +*/ +struct Hit { + static void setup(); + static void teardown(); +}; + +/** +* post-reward monitoring period for the Pair mode. +*/ +struct Monitored { + static void setup(); + static void teardown(); +}; + +/** * used to present reward-related conditioning cue(s) (visual, auditory). * necessary whisk/lick/stimulus-related callbacks are also used. */ @@ -49,8 +75,8 @@ }; /** -* if the animal licked during Cued/Monitor period, the automaton transits to this state. -* unless it is in the Condition mode, reward is delivered here. +* if the animal licked during Paired/Cued/Monitor period, the automaton transits to this state. +* unless it is in the Paired mode, reward is delivered here. */ struct WithResp { static void setup();
--- a/task.cpp Sun Oct 14 14:45:02 2018 +0000 +++ b/task.cpp Thu Dec 13 07:18:43 2018 +0000 @@ -25,7 +25,8 @@ { } const char ModeSelection::CMD_ID_MODE = '_'; -const char ModeSelection::CMD_MODE_CONDITION = 'C'; +const char ModeSelection::CMD_MODE_PAIR = 'P'; +const char ModeSelection::CMD_MODE_STAGE = 'S'; const char ModeSelection::CMD_MODE_REPORT = 'R'; const char ModeSelection::CMD_MODE_ASSOCIATE = 'A'; const char ModeSelection::CMD_MODE_MOTION = 'M'; @@ -42,8 +43,9 @@ bool ModeSelection::parse(const char& c) { switch(c) { - case CMD_MODE_CONDITION: value = Condition; return true; + case CMD_MODE_PAIR: value = Pair; return true; case CMD_MODE_REPORT: value = Report; return true; + case CMD_MODE_STAGE: value = Stage; return true; case CMD_MODE_ASSOCIATE: value = Associate; return true; case CMD_MODE_MOTION: value = Motion; return true; case CMD_MODE_MOTION_ALT: value = MotionAlt; return true; @@ -54,8 +56,9 @@ bool ModeSelection::writeSettings() { #define WRITE(CHR, VAL) if (value == (VAL)) { IO::write("[%c]",CHR); } else { IO::write("%c",CHR); } - WRITE(CMD_MODE_CONDITION, Condition) + WRITE(CMD_MODE_PAIR, Pair) WRITE(CMD_MODE_REPORT, Report) + WRITE(CMD_MODE_STAGE, Stage) WRITE(CMD_MODE_ASSOCIATE, Associate) WRITE(CMD_MODE_MOTION, Motion) WRITE(CMD_MODE_MOTION_ALT, MotionAlt)
--- a/task.h Sun Oct 14 14:45:02 2018 +0000 +++ b/task.h Thu Dec 13 07:18:43 2018 +0000 @@ -12,13 +12,13 @@ #define CMD_CLEAR_INDEX 'O' #define CMD_EXECUTE 'X' -#define CFG_DELAY_MIN 'm', (3000) +#define CFG_DELAY_MIN 'm', (1000) #define CFG_DELAY_VAR 'd', (3000) -#define CFG_PREP_DUR 'p', (1500) +#define CFG_PREP_DUR 'p', (500) #define CFG_AUD_DUR 'a', (5000) #define CFG_AUD_FREQ 'f', (4) -#define CFG_RESP_DUR 'y', (1000) -#define CFG_POST_DUR 'n', (4000) +#define CFG_RESP_DUR 'y', (1500) +#define CFG_POST_DUR 'n', (3000) #define CFG_REWARD_DUR 'r', (50) #define CFG_ONSET_MIN 'o', (500) #define CFG_ONSET_STEPS 's', (10) @@ -28,8 +28,9 @@ #define CFG_LICK_DEBOUNCE 'l', (80) enum Mode { - Condition, + Pair, Report, + Stage, Associate, Motion, MotionAlt @@ -38,7 +39,8 @@ struct ModeSelection: public config::CommandResponder { static const char CMD_ID_MODE; - static const char CMD_MODE_CONDITION; + static const char CMD_MODE_PAIR; + static const char CMD_MODE_STAGE; static const char CMD_MODE_REPORT; static const char CMD_MODE_ASSOCIATE; static const char CMD_MODE_MOTION; @@ -89,7 +91,7 @@ Property<uint16_t> vis_dur_ms; // the duration for the (passive) visual cue. - Property<uint16_t> vis_test_every; // the frequency of 'test' trials during the Condition mode. + Property<uint16_t> vis_test_every; // the frequency of 'test' trials during the Pair/Report modes. Property<uint16_t> whisk_debounce_ms; // the debounce period for whisking events. @@ -101,7 +103,7 @@ Action run; - explicit Task(const Mode& mode=Condition); + explicit Task(const Mode& mode=Pair); void parseFromSerial();
--- a/trial.cpp Sun Oct 14 14:45:02 2018 +0000 +++ b/trial.cpp Thu Dec 13 07:18:43 2018 +0000 @@ -17,7 +17,7 @@ } void TrialFlag::writeToSerial(const Task& task) { - if (task.mode.value == Condition) + if (task.mode.value == Pair) { if (reset) { IO::write("reset"); @@ -67,7 +67,7 @@ // configure cued state duration + visual stimulus position (if any) switch (task.mode.value) { - case Condition: + case Pair: flag.rewarded = (index > 0); visualOut.setEnabled(true); visualOut.setOnset(0); @@ -78,6 +78,7 @@ visualOut.setOnset(0); visualOut.setDuration(ms_to_us(task.vis_dur_ms.value)); break; + case Stage: case Associate: assignAssociative(task); break; @@ -89,9 +90,10 @@ // assign Auditory cues (if any) switch (task.mode.value) { - case Condition: + case Pair: case Report: break; + case Stage: case Associate: case Motion: case MotionAlt: @@ -126,7 +128,7 @@ 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); + vis_onset_us = (uint32_t)((onset_vrng*onset_step/nsteps) + minonset_us); visualOut.setEnabled(index > 0); visualOut.setOnset(vis_onset_us); @@ -163,9 +165,11 @@ IO::write(";index%u;wait%u", index, waiting); - if (task.mode.value == Associate) { + switch (task.mode.value) { + case Stage: + case Associate: if ((visualOut.isEnabled()) && (!flag.reset)) { - IO::write(";visual%u",(vis_onset_us/1000)); + IO::write(";visual%u",(uint16_t)(vis_onset_us/1000)); } }
--- a/trial.h Sun Oct 14 14:45:02 2018 +0000 +++ b/trial.h Thu Dec 13 07:18:43 2018 +0000 @@ -10,7 +10,7 @@ struct TrialFlag { bool cued; bool responded; - bool rewarded; // only used during Condition mode + bool rewarded; // only used during Pair mode bool reset; TrialFlag(): cued(false), responded(false), rewarded(false), reset(false) {} @@ -22,7 +22,7 @@ struct Trial { /** - * the trial count used during the Condition/Report/Associate modes + * the trial count used during the Pair/Report/Associate modes */ uint8_t index;