fast-feedback virtual target task code on STM Nucleo

Dependencies:   mbed

Files at this revision

API Documentation at this revision

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

states.cpp Show annotated file Show diff for this revision Revisions of this file
states.h Show annotated file Show diff for this revision Revisions of this file
task.cpp Show annotated file Show diff for this revision Revisions of this file
task.h Show annotated file Show diff for this revision Revisions of this file
trial.cpp Show annotated file Show diff for this revision Revisions of this file
trial.h Show annotated file Show diff for this revision Revisions of this file
--- 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;