00001 /*!@file Psycho/Staircase.C A staircase procedure for psychophysical thresholds */ 00002 00003 // //////////////////////////////////////////////////////////////////// // 00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2003 // 00005 // by the University of Southern California (USC) and the iLab at USC. // 00006 // See http://iLab.usc.edu for information about this project. // 00007 // //////////////////////////////////////////////////////////////////// // 00008 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00009 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00010 // in Visual Environments, and Applications'' by Christof Koch and // 00011 // Laurent Itti, California Institute of Technology, 2001 (patent // 00012 // pending; application number 09/912,225 filed July 23, 2001; see // 00013 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00014 // //////////////////////////////////////////////////////////////////// // 00015 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00016 // // 00017 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00018 // redistribute it and/or modify it under the terms of the GNU General // 00019 // Public License as published by the Free Software Foundation; either // 00020 // version 2 of the License, or (at your option) any later version. // 00021 // // 00022 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00023 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00024 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00025 // PURPOSE. See the GNU General Public License for more details. // 00026 // // 00027 // You should have received a copy of the GNU General Public License // 00028 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00029 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00030 // Boston, MA 02111-1307 USA. // 00031 // //////////////////////////////////////////////////////////////////// // 00032 // 00033 // Primary maintainer for this file: Laurent Itti <itti@usc.edu> 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Psycho/Staircase.C $ 00035 // $Id: Staircase.C 8264 2007-04-17 21:43:10Z rjpeters $ 00036 // 00037 00038 #include "Psycho/Staircase.H" 00039 00040 #include "Component/OptionManager.H" 00041 #include "Util/MathFunctions.H" 00042 #include "Util/sformat.H" 00043 00044 #include <fstream> 00045 00046 // ###################################################################### 00047 Staircase::Staircase(OptionManager& mgr, const std::string& descrName, 00048 const std::string& tagName) : 00049 ModelComponent(mgr, descrName, tagName), 00050 itsInitial("InitialValue", this, 1.0), 00051 itsDelta("DeltaValue", this, 0.1), 00052 itsMini("MinValue", this, 0.0), 00053 itsMaxi("MaxValue", this, 1.0), 00054 itsNright("Nright", this, 4), 00055 itsNwrong("Nwrong", this, 2), 00056 itsFileName("FileName", this, std::string("staircase-")+tagName+".txt"), 00057 itsTrial(0), itsValue(0.0), 00058 itsTimer(1000000), // use microsecond resolution 00059 itsEvents(), itsNr(0), itsNw(0), itsExpectedResponse(false) 00060 { } 00061 00062 // ###################################################################### 00063 Staircase::~Staircase() 00064 { } 00065 00066 // ###################################################################### 00067 void Staircase::start2() 00068 { 00069 initRandomNumbers(); 00070 00071 itsValue = itsInitial.getVal(); itsTimer.reset(); 00072 time_t ti = time(NULL); 00073 pushEvent(sformat("##### START %s #####", ctime(&ti))); 00074 } 00075 00076 // ###################################################################### 00077 void Staircase::reset1() 00078 { 00079 itsTrial = 0; itsValue = itsInitial.getVal(); 00080 itsTimer.reset(); itsNr = 0; itsNw = 0; 00081 time_t ti = time(NULL); 00082 pushEvent(sformat("##### RESET %s #####", ctime(&ti))); 00083 00084 ModelComponent::reset1(); 00085 } 00086 00087 // ###################################################################### 00088 void Staircase::stop1() 00089 { 00090 // let's save the events: 00091 if (itsEvents.empty() == false && itsFileName.getVal().length() > 1) 00092 { 00093 std::ofstream ofs(itsFileName.getVal().c_str()); 00094 if (!ofs.is_open()) 00095 { 00096 LERROR("Couldn't open file '%s' for writing.", 00097 itsFileName.getVal().c_str()); 00098 return; 00099 } 00100 00101 std::list<StaircaseEvent>::const_iterator itr = itsEvents.begin(); 00102 while (itr != itsEvents.end()) { 00103 int usec = int(itr->tim % 1000ULL); 00104 int msec = int((itr->tim / 1000ULL) % 1000ULL); 00105 int sec = int((itr->tim / 1000000ULL) % 60ULL); 00106 int minu = int((itr->tim / 60000000ULL) % 60ULL); 00107 int hour = int(itr->tim / 3600000000ULL); 00108 char tim[256]; sprintf(tim, "%03d:%02d:%02d.%03d.%03d", 00109 hour, minu, sec, msec, usec); 00110 ofs << tim << " " << itr->descrip; 00111 if (itr->trial >= 0) { 00112 ofs << " Trial = " << itr->trial << " Value = " << itr->val; 00113 if (itr->descrip.compare("end ") == 0) 00114 ofs << " Response = " << (itr->response ? "Correct" : "Incorrect"); 00115 } 00116 ofs << std::endl; 00117 itr ++; 00118 } 00119 ofs.close(); 00120 LINFO("Saved data to '%s'", itsFileName.getVal().c_str()); 00121 } 00122 } 00123 00124 // ###################################################################### 00125 void Staircase::getValues(double& value1, double& value2) 00126 { 00127 // randomly pick a trial type 00128 if (randomDouble() < 0.5) 00129 { 00130 // nothing-then-something: 00131 value1 = 0.0; value2 = itsValue; itsExpectedResponse = true; 00132 } 00133 else 00134 { 00135 // something-then-nothing: 00136 value1 = itsValue; value2 = 0.0; itsExpectedResponse = false; 00137 } 00138 00139 // log the setup: 00140 pushEvent("begin", itsTrial, itsValue, true); 00141 } 00142 00143 // ###################################################################### 00144 void Staircase::setResponse(const bool response) 00145 { 00146 bool correct = (response == itsExpectedResponse); 00147 00148 // log the current value of the parameter and the corresponding 00149 // response obtained for that value: 00150 pushEvent("end ", itsTrial, itsValue, correct); 00151 00152 // compute the next value to use: 00153 if (correct) { itsNr ++; itsNw = 0; } // correct answer 00154 else { itsNw ++; itsNr = 0; } // incorrect answer 00155 00156 if (itsNr >= itsNright.getVal()) 00157 { itsValue -= itsDelta.getVal(); itsNr = 0; } 00158 if (itsNw >= itsNwrong.getVal()) 00159 { itsValue += itsDelta.getVal(); itsNw = 0; } 00160 00161 if (itsValue < itsMini.getVal()) itsValue = itsMini.getVal(); 00162 if (itsValue > itsMaxi.getVal()) itsValue = itsMaxi.getVal(); 00163 00164 itsTrial ++; 00165 } 00166 00167 // ###################################################################### 00168 void Staircase::pushEvent(const std::string& msg, const int trial, 00169 const double val, const bool response) 00170 { 00171 StaircaseEvent evt; 00172 evt.descrip = msg; evt.tim = itsTimer.get(); 00173 evt.trial = trial; evt.val = val; evt.response = response; 00174 itsEvents.push_back(evt); 00175 } 00176 00177 // ###################################################################### 00178 /* So things look consistent in everyone's emacs... */ 00179 /* Local Variables: */ 00180 /* indent-tabs-mode: nil */ 00181 /* End: */