00001 /*!@file AppPsycho/psycho-spatial-memory-num-counting.C Psychophysics test to measure the effect of executive tasks on spatial memory */ 00002 00003 // //////////////////////////////////////////////////////////////////// // 00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the // 00005 // 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/AppPsycho/psycho-spatial-memory-num-counting.C $ 00035 // $Id: psycho-spatial-memory-num-counting.C 13059 2010-03-26 08:14:32Z itti $ 00036 // 00037 00038 #include "Component/ModelManager.H" 00039 #include "Image/Image.H" 00040 #include "Psycho/PsychoDisplay.H" 00041 #include "Psycho/EyeTrackerConfigurator.H" 00042 #include "Psycho/EyeTracker.H" 00043 #include "Psycho/PsychoOpts.H" 00044 #include "Component/EventLog.H" 00045 #include "Component/ComponentOpts.H" 00046 #include "Raster/Raster.H" 00047 #include "Util/MathFunctions.H" 00048 #include "Util/Types.H" 00049 #include "GameBoard/basic-graphics.H" 00050 #include <sys/types.h> 00051 #include <dirent.h> 00052 #include <errno.h> 00053 #include <vector> 00054 #include <string> 00055 #include <iostream> 00056 #include <SDL/SDL.h> 00057 #include <SDL/SDL_image.h> 00058 #include <SDL/SDL_mixer.h> 00059 #include <stdio.h> 00060 #include <stdlib.h> 00061 #include <sstream> 00062 #include <time.h> 00063 #include "Image/DrawOps.H" 00064 #include "GameBoard/resize.h" 00065 #include <iostream> 00066 #include <fstream> 00067 #include <set> 00068 #include <algorithm> 00069 #include <ctime> 00070 00071 #ifndef INVT_HAVE_LIBSDL_IMAGE 00072 #include <cstdio> 00073 int main() 00074 { 00075 fprintf(stderr, "The SDL_image library must be installed to use this program\n"); 00076 return 1; 00077 } 00078 00079 #else 00080 00081 00082 00083 using namespace std; 00084 00085 // ###################################################################### 00086 00087 ModelManager manager("Psycho-Spatial-Memory"); 00088 nub::soft_ref<PsychoDisplay> d(new PsychoDisplay(manager)); 00089 map<uint,uint> testMap ; 00090 map<string,string> argMap ; 00091 map<string,vector<SDL_Rect*>*> clipsmap; 00092 00093 00094 ////////////////////////////////////////////// 00095 // a functionf for stringigying things 00096 ////////////////////////////////////////////// 00097 template <class T> std::string stringify(T i) 00098 { 00099 ostringstream o ; 00100 o << i ; 00101 return o.str(); 00102 } 00103 00104 00105 double getAvarage(vector<long> v){ 00106 double f = 0.0 ; 00107 for( uint i = 0 ; i < v.size() ; i++ ){ 00108 f += v[i] ; 00109 } 00110 if (v.size()!=0) return f/v.size() ; 00111 return -1 ; 00112 } 00113 00114 double getVariance(vector<long> v){ 00115 double m = getAvarage(v); 00116 double var = 0.0 ; 00117 for( uint i = 0 ; i < v.size(); i++ ){ 00118 var += (v[i]-m)*(v[i]-m) ; 00119 } 00120 if (v.size()!=0) return var/v.size() ; 00121 return -1 ; 00122 } 00123 00124 bool itIsInThere(int x , vector<int> bag){ 00125 for( uint i=0 ; i < bag.size(); i++ ){ 00126 if(x == bag[i]) return true ; 00127 } 00128 return false ; 00129 } 00130 00131 00132 //////////////////////////////////////////////////////////////////// 00133 //// gets a string as the argument and returns a string composed of 00134 //// characters of the first string sorted in the ascending order 00135 /////////////////////////////////////////////////////////////////// 00136 string ascSort(string st) 00137 { 00138 string res = "" ; 00139 vector<string> v = vector<string>(); 00140 for(uint i = 0 ; i < st.size() ; i++) v.push_back(st.substr(i,1)) ; 00141 00142 std::sort(v.begin(), v.end()); 00143 00144 for ( uint i = 0 ; i < v.size() ; i++ ){ 00145 res += v[i] ; 00146 } 00147 return res; 00148 } 00149 00150 00151 00152 //////////////////////////////////////////////////////////////////// 00153 //// gets a string as the argument and returns a string composed of 00154 //// characters of the first string sorted in the descending order 00155 /////////////////////////////////////////////////////////////////// 00156 string desSort(string st) 00157 { 00158 string res = "" ; 00159 vector<string> v = vector<string>(); 00160 for(uint i = 0 ; i < st.size() ; i++) v.push_back(st.substr(i,1)) ; 00161 std::sort(v.begin(), v.end()); 00162 std::reverse(v.begin(), v.end()); 00163 for ( uint i = 0 ; i < v.size() ; i++ ){ 00164 res += v[i] ; 00165 } 00166 return res; 00167 } 00168 00169 00170 //////////////////////////////////////////////////////// 00171 ///// simply generates a sequence of characters out of master string 00172 //////////////////////////////////////////////////////// 00173 00174 string getARandomString(uint l, string alphabet="0123456789" ){ 00175 00176 string test = string("") ; 00177 string retString ; 00178 test = "" ; 00179 string tp = string("") ; 00180 vector<int> pickedones = vector<int>() ; 00181 for(uint i = 0 ; i < l ; i++){ 00182 int nd; 00183 do{ nd= rand()% alphabet.size() ; }while(itIsInThere(nd,pickedones)) ; 00184 pickedones.push_back(nd); 00185 tp = alphabet.substr(nd,1) ; 00186 test += tp ; 00187 } 00188 retString = test ; 00189 return retString ; 00190 } 00191 00192 /////////////////////////////////////////////////////// 00193 //this function is not called in this program, but it generates a random string and it will show it in 00194 //a random place on the screen. 00195 ////////////////////////////////////////////////////// 00196 string digitMemorizationTask(uint l, string alphabet="0123456789" , int displayFrame = 10 ){ 00197 d->clearScreen() ; 00198 vector<int> pickedones = vector<int>() ; 00199 string test = string("") ; 00200 string tp = string("") ; 00201 for(uint i = 0 ; i < l ; i++){ 00202 int nd; 00203 do{ nd= rand()% alphabet.size() ; }while(itIsInThere(nd,pickedones) && pickedones.size() <= alphabet.size()) ; 00204 pickedones.push_back(nd); 00205 tp = alphabet.substr(nd,1) ; 00206 test += tp ; 00207 } 00208 d->displayText(test,true,0) ; 00209 d->waitFrames(displayFrame) ; 00210 d->clearScreen() ; 00211 return test ; 00212 } 00213 00214 00215 //////////////////////////////////////////////////////// 00216 ///////this will change the order of elements in a vector to a random order 00217 //////////////////////////////////////////////////////// 00218 void scramble(vector<string>& v){ 00219 vector<string> tv = vector<string>() ; 00220 while(v.size()>0){ 00221 tv.push_back(v[0]); 00222 v.erase(v.begin()); 00223 } 00224 int i = 0 ; 00225 while(tv.size()>0){ 00226 i = rand()%tv.size() ; 00227 v.push_back(tv[i]); 00228 tv.erase(tv.begin()+i); 00229 } 00230 } 00231 00232 void scramble(vector<int>& v){ 00233 vector<int> tv = vector<int>() ; 00234 while(v.size()>0){ 00235 tv.push_back(v[0]); 00236 v.erase(v.begin()); 00237 } 00238 int i = 0 ; 00239 while(tv.size()>0){ 00240 i = rand()%tv.size() ; 00241 v.push_back(tv[i]); 00242 tv.erase(tv.begin()+i); 00243 } 00244 } 00245 00246 //////////////////////////////////////////////////////////////// 00247 ////This is our button factory 00248 //////////////////////////////////////////////////////////////// 00249 SDL_Surface* getButtonImage(string label , PixRGB<byte> txtcolor=PixRGB<byte>(0,0,0) , PixRGB<byte> bgcolor=PixRGB<byte>(255,255,255) ,Point2D<int> size = Point2D<int>(100,100) ,PixRGB<byte> bordercolor=PixRGB<byte>(0,0,0) , int border=3){ 00250 Image<PixRGB<byte> > textIm(d->getWidth(),d->getHeight(),ZEROS); 00251 textIm.clear(bgcolor); 00252 writeText(textIm, Point2D<int>((size.i - label.length()*10)/2,(size.j-20) /2),label.c_str(),txtcolor,bgcolor); 00253 SDL_Surface *surf = d->makeBlittableSurface(textIm , true); 00254 Uint32 bc = d->getUint32color(bordercolor); 00255 drawRectangle(surf,bc,0,0,size.i -1,size.j -1 ,border); 00256 SDL_Surface* blank =getABlankSurface(size.i , size.j); 00257 SDL_Rect clip; 00258 clip.x = 0 ; 00259 clip.y = 0 ; 00260 clip.w = size.i ; 00261 clip.h = size.j ; 00262 apply_surface(0,0,*surf,*blank,clip); 00263 dumpSurface(surf) ; 00264 return blank ; 00265 } 00266 00267 //////////////////////////////////////////////////////////////////////// 00268 ////This is the function for creating the keypad, in fact it generates 00269 ////12 buttons and associates the actions to the region for each button 00270 //////////////////////////////////////////////////////////////////////// 00271 00272 SDL_Surface* getKeyPad(string alphabet,map<string , SDL_Rect>& buttmap){ 00273 SDL_Surface* pad= getABlankSurface(d->getWidth()/4,d->getHeight()/3); 00274 SDL_Rect clip; 00275 clip.x=0; 00276 clip.y=0; 00277 int numofrows = alphabet.size()/3 +1; 00278 if(alphabet.size()%3 != 0 ) numofrows++ ; 00279 int numofcolumns = 3 ; 00280 clip.w= pad->w / numofcolumns ; 00281 clip.h = pad->h / numofrows ; 00282 00283 //keys for 1 to 9 00284 for( int i = 0 ; i < numofrows*3 ; i++){ 00285 SDL_Surface* but ; 00286 if((uint)i < alphabet.size()){ 00287 but = getButtonImage(alphabet.substr(i,1),PixRGB<byte>(0,0,0),PixRGB<byte>(255,255,255),Point2D<int>(pad->w / numofcolumns , pad->h / numofrows),PixRGB<byte>(255, 98 , 25),3); 00288 }else{ 00289 but = getButtonImage(" ",PixRGB<byte>(0,0,0),PixRGB<byte>(255,255,255),Point2D<int>(pad->w / numofcolumns , pad->h / numofrows),PixRGB<byte>(255, 98 , 25),3); 00290 } 00291 00292 SDL_Rect cl ; 00293 cl.x = ((i)%numofcolumns)*(pad->w)/numofcolumns ; cl.y= ((i)/numofcolumns)*((pad->h)/numofrows) ; 00294 cl.w = clip.w ; 00295 cl.h = clip.h ; 00296 apply_surface( cl.x , cl.y ,*but,*pad,clip); 00297 if((uint)i < alphabet.size()) buttmap[alphabet.substr(i,1)] = cl ; 00298 dumpSurface(but); 00299 } 00300 SDL_Rect cl1 ; 00301 cl1.x = 0 ; cl1.y= (numofrows-1)*((pad->h)/numofrows) ; 00302 cl1.w = clip.w ; 00303 cl1.h = clip.h ; 00304 buttmap["!"] = cl1 ; 00305 SDL_Surface* but = getButtonImage(string("<-"),PixRGB<byte>(0,0,0),PixRGB<byte>(255,255,255),Point2D<int>(pad->w / numofcolumns , pad->h / numofrows),PixRGB<byte>(255, 98 , 25),3); 00306 apply_surface(0, (numofrows-1)*((pad->h)/numofrows),*but,*pad,clip); 00307 dumpSurface(but); 00308 SDL_Rect cl2 ; 00309 cl2.x = (pad->w)/numofcolumns ; cl2.y= (numofrows-1)*((pad->h)/numofrows) ; 00310 cl2.w = clip.w ; 00311 cl2.h = clip.h ; 00312 buttmap[" "] = cl2 ; 00313 but = getButtonImage(string("spc"),PixRGB<byte>(0,0,0),PixRGB<byte>(255,255,255),Point2D<int>(pad->w / numofcolumns , pad->h / numofrows),PixRGB<byte>(255, 98 , 25),3); 00314 apply_surface((pad->w)/numofcolumns, (numofrows-1)*((pad->h)/numofrows),*but,*pad,clip); 00315 dumpSurface(but); 00316 SDL_Rect cl3 ; 00317 cl3.x = 2*(pad->w)/numofcolumns ; cl3.y= (numofrows-1)*((pad->h)/numofrows) ; 00318 cl3.w = clip.w ; 00319 cl3.h = clip.h ; 00320 buttmap["*"] = cl3 ; 00321 but = getButtonImage(string("Ok"),PixRGB<byte>(0,0,0),PixRGB<byte>(255,255,255),Point2D<int>(pad->w / numofcolumns , pad->h / numofrows),PixRGB<byte>(255, 98 , 25),3); 00322 apply_surface(2*(pad->w)/numofcolumns, (numofrows-1)*((pad->h)/numofrows),*but,*pad,clip); 00323 dumpSurface(but); 00324 return pad ; 00325 } 00326 00327 00328 00329 00330 /////////////////////////////////////////////////////////////////////////// 00331 /////this function listens to mouse clicks and then finds the region of the screen 00332 /////associated with the action, buttmap is the map of the region, offset is the offset of 00333 /////buttons 00334 /////////////////////////////////////////////////////////////////////////// 00335 string getPressedButtonCommand(map<string , SDL_Rect>& buttmap,Point2D<int> offset=Point2D<int>(0,0)){ 00336 int quit = 0 ; 00337 string s ; 00338 SDL_Event event ; 00339 while( quit!=2 ){ 00340 while( SDL_PollEvent( &event ) ) { 00341 if(event.type == SDL_MOUSEBUTTONDOWN && event.button.button == SDL_BUTTON_LEFT ){ 00342 for( map<string , SDL_Rect>::iterator it = buttmap.begin() ; it!=buttmap.end() ; ++it){ 00343 if(event.button.x >= (it->second).x + offset.i && event.button.x <= (it->second).x + (it->second).w + offset.i && event.button.y >= (it->second).y+ offset.j && event.button.y <= (it->second).y + (it->second).h + offset.j) { 00344 quit = 2 ; 00345 s = it->first ; 00346 break; 00347 } 00348 00349 } 00350 } 00351 00352 } 00353 } 00354 return s ; 00355 00356 } 00357 00358 00359 //////////////////////////////////////////////////// 00360 ////This function creates a virtual keypad, creates a map of buttons 00361 ////and their representation area and listens to the button press and at 00362 ////the end returns the keyed digits 00363 //////////////////////////////////////////////////// 00364 string getDigitSequenceFromSubject(string alphabet="0123456789" , uint maxl = 7 , string message=""){ 00365 d->showCursor(true) ; 00366 //let's creat a map to map actions to regions of the screen, each region is represented as an SDL_Rect 00367 map<string , SDL_Rect>* buttmap = new map<string , SDL_Rect>(); 00368 //now let's get the keypad surface while we get the actions map to regions 00369 SDL_Surface * keypad = getKeyPad(alphabet,*buttmap); 00370 //this will be the offset of displaying the keypad on the screen 00371 SDL_Rect offset ; 00372 offset.x = (d->getWidth() - keypad->w) /2; 00373 offset.y = (d-> getHeight() - keypad->h) /2; 00374 //d->displayText(message, Point2D<int>(d->getWidth()/3,d->getHeight()*2 /16) , PixRGB<byte>(0,0,0) ,PixRGB<byte>(127,127,127)) ; 00375 SDL_Surface* msgp = getButtonImage(message ,PixRGB<byte>(195,60,12) ,PixRGB<byte>(127,127,127) ,Point2D<int>(d->getWidth()/6,d->getHeight() /15) ,PixRGB<byte>(127,127,127) , 4) ; 00376 SDL_Rect msgoffs ; msgoffs.x = (d->getWidth() - msgp->w) /2 ; msgoffs.y = 2*d->getHeight()/9 ; 00377 d->displaySDLSurfacePatch(msgp , &msgoffs , NULL , -2 , false ,true ) ; 00378 dumpSurface(msgp) ; 00379 //now let's display the keypad 00380 d->displaySDLSurfacePatch(keypad , &offset,NULL , -2,false, true); 00381 //this will hold the final string keyed be the subject 00382 string p = string("") ; 00383 //this is a temporary string holding the last action related to the pressed key 00384 string tp = string(""); 00385 //now let's record subject's key press 00386 while( tp.compare("*")!=0 ){ 00387 //this button is actually the display for the current string 00388 SDL_Surface* dp = getButtonImage(p ,PixRGB<byte>(195,60,12) ,PixRGB<byte>(255,255,255) ,Point2D<int>(d->getWidth()/6,d->getHeight() /15) ,PixRGB<byte>(0,25,180) , 4) ; 00389 SDL_Rect offs ; offs.x = (d->getWidth() - dp->w) /2 ; offs.y = d->getHeight()/6 ; 00390 d->displaySDLSurfacePatch(dp , &offs , NULL , -2 , false ,true ) ; 00391 //now let's listen to button events 00392 tp = getPressedButtonCommand(*buttmap,Point2D<int>(offset.x,offset.y)) ; 00393 dumpSurface(dp) ; 00394 if(tp.compare("!")==0 && p.size()>=0 ) { 00395 if (p.size()>0) p = p.substr(0,p.size()-1) ; 00396 }else{ 00397 if(p.size() < maxl && tp.compare("*")!=0) { 00398 p +=tp ; 00399 } 00400 00401 } 00402 00403 } 00404 00405 buttmap = 0 ; 00406 dumpSurface(keypad) ; 00407 d->clearScreen() ; 00408 return p ; 00409 00410 } 00411 00412 00413 00414 /////////////////////////////////////////////////////////////// 00415 //////gets the test string, answer and the mode and identifies if 00416 //////the answer matches the thing it should be, mode=0 checks if 00417 //////if the answer and test string simply the same, mode=1 matches 00418 //////the answer against the ascending sorted string of the test string 00419 //////mode=2 compares the answer against the descending sorted of 00420 //////the test string 00421 /////////////////////////////////////////////////////////////// 00422 bool isAnswerCorrect(string test , string answer , int mode){ 00423 00424 if(mode == 0 && answer.compare(test)==0) return true ; 00425 00426 if(mode == 1 && answer.compare(ascSort(test))==0) return true ; 00427 00428 if(mode == 2 && answer.compare(desSort(test))==0) return true ; 00429 00430 return false; 00431 } 00432 00433 00434 00435 int addArgument(const string st,const string delim="="){ 00436 int i = st.find(delim) ; 00437 argMap[st.substr(0,i)] = st.substr(i+1); 00438 00439 return 0 ; 00440 } 00441 00442 std::string getArgumentValue(string arg){ 00443 return argMap[arg] ; 00444 } 00445 00446 00447 void displayWholeNumber(string s , int onsetTime , int wsd){ 00448 int x = (d->getWidth()-s.size()*wsd)/2 ; 00449 int y = (d->getHeight())/2 -10; 00450 Image<PixRGB<byte> > textIm(d->getWidth(),d->getHeight(),ZEROS); 00451 textIm.clear(PixRGB<byte>(128,128,128)); 00452 for( uint k = 0 ; k < s.size() ; k++ ){ 00453 // d->displayText(s.substr(k,1),Point2D<int>(x,y+k*10),PixRGB<byte>(0,0,0),PixRGB<byte>(128,128,128),true) ; 00454 writeText(textIm, Point2D<int>(x+k*wsd,y),s.substr(k,1).c_str(),PixRGB<byte>(0,0,0),PixRGB<byte>(128,128,128)); 00455 } 00456 SDL_Surface *surf = d->makeBlittableSurface(textIm , true); 00457 SDL_Rect offs ; offs.x = 0 ; offs.y = 0 ; 00458 d->displaySDLSurfacePatch(surf , &offs , NULL , -2 , false ,true ) ; 00459 dumpSurface(surf); 00460 d->waitFrames(onsetTime); 00461 d->clearScreen() ; 00462 } 00463 00464 void displayWholeNumberVertically(string s , int onsetTime , int wsd){ 00465 int x = (d->getWidth())/2 ; 00466 int y = (d->getHeight()-s.size()*wsd)/2 ; 00467 Image<PixRGB<byte> > textIm(d->getWidth(),d->getHeight(),ZEROS); 00468 textIm.clear(PixRGB<byte>(128,128,128)); 00469 for( uint k = 0 ; k < s.size() ; k++ ){ 00470 // d->displayText(s.substr(k,1),Point2D<int>(x,y+k*10),PixRGB<byte>(0,0,0),PixRGB<byte>(128,128,128),true) ; 00471 writeText(textIm, Point2D<int>(x,y+k*wsd),s.substr(k,1).c_str(),PixRGB<byte>(0,0,0),PixRGB<byte>(128,128,128)); 00472 } 00473 SDL_Surface *surf = d->makeBlittableSurface(textIm , true); 00474 SDL_Rect offs ; offs.x = 0 ; offs.y = 0 ; 00475 d->displaySDLSurfacePatch(surf , &offs , NULL , -2 , false ,true ) ; 00476 dumpSurface(surf); 00477 d->waitFrames(onsetTime); 00478 d->clearScreen() ; 00479 } 00480 00481 void displayRandom(string s , int onsetTime){ 00482 for( uint k = 0 ; k < s.size() ; k++ ){ 00483 int x = 9*d->getWidth()/20 + rand()%(d->getWidth()/10); 00484 int y = 9*d->getHeight()/20 + rand()%(d->getHeight()/10) ; 00485 d->displayText(s.substr(k,1),Point2D<int>(x,y),PixRGB<byte>(0,0,0),PixRGB<byte>(128,128,128),true) ; 00486 d->waitFrames(onsetTime); 00487 d->clearScreen() ; 00488 } 00489 } 00490 00491 void displayLinear(string s , int onsetTime){ 00492 int x = (d->getWidth()-s.size()*10)/2 ; 00493 int y = d->getHeight()/2 - 10; 00494 for( uint k = 0 ; k < s.size() ; k++ ){ 00495 d->displayText(s.substr(k,1),Point2D<int>(x+k*10,y),PixRGB<byte>(0,0,0),PixRGB<byte>(128,128,128),true) ; 00496 d->waitFrames(onsetTime); 00497 d->clearScreen() ; 00498 } 00499 } 00500 00501 void displayLinearReverse(string s , int onsetTime){ 00502 int x = (d->getWidth()-s.size()*10)/2 ; 00503 int y = d->getHeight()/2 - 10; 00504 for( uint k = 0 ; k < s.size() ; k++ ){ 00505 d->displayText(s.substr(k,1),Point2D<int>(x+(s.size()-k)*10,y),PixRGB<byte>(0,0,0),PixRGB<byte>(128,128,128),true) ; 00506 d->waitFrames(onsetTime); 00507 d->clearScreen() ; 00508 } 00509 } 00510 void displayLinearRandom(string s , int onsetTime){ 00511 int x = (d->getWidth()-s.size()*10)/2 ; 00512 int y = d->getHeight()/2 - 10; 00513 for( uint k = 0 ; k < s.size() ; k++ ){ 00514 d->displayText(s.substr(k,1),Point2D<int>(x+ (random()%s.size())*10,y),PixRGB<byte>(0,0,0),PixRGB<byte>(128,128,128),true) ; 00515 d->waitFrames(onsetTime); 00516 d->clearScreen() ; 00517 } 00518 } 00519 00520 void displayLinearRandomVertically(string s , int onsetTime){ 00521 int x = (d->getWidth())/2 ; 00522 int y = (d->getHeight()-s.size()*10)/2 - 10; 00523 for( uint k = 0 ; k < s.size() ; k++ ){ 00524 d->displayText(s.substr(k,1),Point2D<int>(x, (random()%s.size())*10+y),PixRGB<byte>(0,0,0),PixRGB<byte>(128,128,128),true) ; 00525 d->waitFrames(onsetTime); 00526 d->clearScreen() ; 00527 } 00528 } 00529 00530 void displayLinearRandomNoRepeating(string s , int onsetTime){ 00531 int x = (d->getWidth()-s.size()*10)/2 ; 00532 int y = d->getHeight()/2 - 10; 00533 for( uint k = 0 ; k < s.size() ; k++ ){ 00534 d->displayText(s.substr(k,1),Point2D<int>(x+ (random()%s.size())*10,y),PixRGB<byte>(0,0,0),PixRGB<byte>(128,128,128),true) ; 00535 d->waitFrames(onsetTime); 00536 d->clearScreen() ; 00537 } 00538 } 00539 00540 //and this is the function which creates and displays a mask of randomly positioned numbers 00541 void showMask(int frames, string alphabet="0123456789"){ 00542 Image<PixRGB<byte> > textIm(d->getWidth(),d->getHeight(),ZEROS); 00543 PixRGB<byte> bgcolor = PixRGB<byte>(128,128,128); 00544 PixRGB<byte> txtcolor = PixRGB<byte>(0,0,0); 00545 textIm.clear(bgcolor); 00546 for(int i = 0 ; i < 200 ; i++) 00547 writeText(textIm, Point2D<int>((int)random()%(d->getWidth()),(int)random()%(d->getHeight())),alphabet.substr(random()%(int)alphabet.size(),1).c_str(),txtcolor,bgcolor); 00548 SDL_Surface *surf = d->makeBlittableSurface(textIm , true); 00549 SDL_Rect offs ; offs.x = 0 ; offs.y = 0 ; 00550 d->displaySDLSurfacePatch(surf , &offs , NULL , -2 , false ,true ) ; 00551 d->waitFrames(frames) ; 00552 d->clearScreen(); 00553 dumpSurface(surf) ; 00554 } 00555 00556 vector<int> spatial_memory_task(int onsetTime, int isd ,int rad=100 , int ma=45 ,int Ma=90){ 00557 00558 int w = d->getWidth(); 00559 int h = d->getHeight(); 00560 int a1 = rand() % 360 ; 00561 int direction = (int)(((rand()%2)-0.5)/0.5); 00562 int a2 =a1 +direction*((rand() % (Ma-ma))+ma); 00563 //d->displayFixation(); 00564 d->displayRedDotFixation(w/2+rad*cos(a1*3.1415/180),h/2 + rad*sin(a1*3.1415/180)); 00565 d->waitFrames(onsetTime); 00566 d->clearScreen(); 00567 //d->displayFixation(); 00568 d->waitFrames(isd); 00569 d->displayRedDotFixation(w/2+rad*cos(a2*3.1415/180),h/2 + rad*sin(a2*3.1415/180)); 00570 d->waitFrames(onsetTime); 00571 d->clearScreen(); 00572 vector<int> angles; 00573 angles.push_back(a1); 00574 angles.push_back(a2); 00575 return angles ; 00576 } 00577 00578 int spatial_memory_retrival(vector<int> angles ,int rad = 100 ){ 00579 int res=0 ; 00580 int flip = rand()%2; 00581 int w = d->getWidth(); 00582 int h = d->getHeight(); 00583 if(flip==0){ 00584 d->displayRedDotFixation(w/2+rad*cos(angles.at(0)*3.1415/180),h/2 + rad*sin(angles.at(0)*3.1415/180)); 00585 d->displayRedDotFixation(w/2+rad*cos(angles.at(1)*3.1415/180),h/2 + rad*sin(angles.at(1)*3.1415/180)); 00586 }else{ 00587 int a1=angles.at(0) % 360; 00588 int a2=angles.at(1) % 360; 00589 if (a2<a1) { 00590 int tmp = a2 ; 00591 a2=a1; 00592 a1=tmp; 00593 } 00594 int sflip=rand()%2; 00595 int del = ((rand()%15) + 30); 00596 if(sflip==0){ 00597 a1=a1-del; 00598 }else{ 00599 a2=a2+del; 00600 } 00601 d->displayRedDotFixation(w/2+rad*cos(a1*3.1415/180),h/2 + rad*sin(a1*3.1415/180)); 00602 d->displayRedDotFixation(w/2+rad*cos(a2*3.1415/180),h/2 + rad*sin(a2*3.1415/180)); 00603 } 00604 d->waitFrames(20); 00605 d->clearScreen(); 00606 string ans = getDigitSequenceFromSubject("yn",1); 00607 if(flip==0){ 00608 res = 0 ; 00609 if(ans.compare("y")==0) res=1; 00610 }else{ 00611 res = 0 ; 00612 if(ans.compare("n")==0) res=1; 00613 } 00614 return res ; 00615 } 00616 00617 vector<int> executive_memory_task(Mix_Music* tone1 , Mix_Music* tone2 ,string initial_string,int counter_length, int min_tone_wait, int max_tone_wait ){ 00618 int t1c = 0 ; 00619 int t2c = 0 ; 00620 vector<int> wFrames; 00621 vector<int> toneSequence ; 00622 vector<int> retVector ; 00623 map<int,string> strMap; 00624 00625 int flag=0; 00626 float milisum=0; 00627 do{ 00628 int rtmp = rand()%(max_tone_wait - min_tone_wait) + min_tone_wait; 00629 milisum += rtmp*33.33 + 240; 00630 if(milisum > counter_length*33.33) { 00631 milisum = milisum -rtmp*33.33 - 240; 00632 flag=1; 00633 }else{ 00634 wFrames.push_back(rtmp); 00635 toneSequence.push_back(rand()%2); 00636 } 00637 }while(flag==0); 00638 int lagFrames = (int)((counter_length - milisum)/33.33); 00639 00640 for(uint i = 0 ; i < wFrames.size() ; i++){ 00641 d->pushEvent("will wait for "+stringify(wFrames[i])); 00642 if(toneSequence[i]==0){ 00643 d->pushEvent("tone1 playing"); 00644 t1c++; 00645 if(Mix_PlayMusic(tone1,0)==-1){return retVector;} 00646 while(Mix_PlayingMusic()==1){} ; 00647 }else{ 00648 d->pushEvent("tone2 playing"); 00649 t2c++ ; 00650 if(Mix_PlayMusic(tone2,0)==-1){return retVector;} 00651 while(Mix_PlayingMusic()==1){} ; 00652 } 00653 d->waitFrames(wFrames[i]); 00654 } 00655 d->waitFrames(lagFrames); 00656 retVector.push_back(t1c); 00657 retVector.push_back(t2c); 00658 d->clearScreen(); 00659 d->displayFixation(); 00660 return retVector ; 00661 00662 } 00663 00664 00665 00666 std::string getUsageComment(){ 00667 00668 string com = string("\nlist of arguments : \n"); 00669 00670 com += "\nlogfile=[logfilename.psy] {default = psycho-sm.psy}\n" ; 00671 com += "\nmemo=[a_string_without_white_space]\n"; 00672 com += "\nstring-size=[>0](the size of counter string){default=4} \n"; 00673 com += "\nsubject=[subject_name] \n" ; 00674 com += "\nnum-of-trials=[>1] (number of trials ) {default=10}\n"; 00675 com += "\nalphabet=[a string of characters](a string of characters){default=abcdefghijklmnopqrstuvwxyz}\n"; 00676 com += "\nmode=[1,2,3](1 for spatial memory task 2 for single counter task, 2 for concurrent task){default=1}\n"; 00677 com += "\nsingle-dot-onset=[>1](number of frames that the single dot should be presented){default=16}\n"; 00678 com += "\ndots_ISI=[>1](number of frames between dots presentations){default=16}\n"; 00679 com += "\ndots_radius=[>1](the radius for circle of dots in pixel){default=100}\n"; 00680 com += "\ndots_min_angle=[>0](minimum angle between dots on the circle){default=45}\n"; 00681 com += "\ndots_max_angle=[>0] (maximum angle between dots on the circle){default=90}\n"; 00682 com += "\nspatial-delay=[>0](number of frames for spatial memory task ){default=180}\n"; 00683 com += "\n spatial-counter-ISI=[>1](numer of frames between last dot presentation and start of counter task){default=16}\n"; 00684 com += "\n counter-length=[>1](total lenght of counter experiment in frames){default=460}\n"; 00685 com += "\n counter-spatial-query-ISI=[>1](the delay between end of counter trail and start of spatial memory query in number of frames){default=16}\n"; 00686 com += "\n spatial-query-length=[>1](the total length for showing the spatial memory test in number of frames){default=60}\n"; 00687 com += "\n spatial-query-counter-query-ISI=[>1](number of frames between the unspeeded spatial memory query and showing the counter query){default=30}\n"; 00688 com += "\n counter-query-length=[>1](number of frames for presenting the counter query){default=60}\n"; 00689 com += "\n cue-onset-frames=[>1](){default=3}\n";; 00690 com += "\nsound-dir=[path to wav files directory]{default=..}\n"; 00691 com += "\ntone1=[a valid file name](name of wav file without extension for tone 1){default=sin}\n"; 00692 com += "\ntone2=[a valid file name](name of wav file without extension for tone 2){default=square}\n"; 00693 com += "\ncue-file=[a valid file name](name of a wav file without extension for the audio cue){default=cue1}\n"; 00694 com += "\nmin-tone-wait=[>1](minimum number of frames waiting on each tone){default=60}\n"; 00695 com += "\nmax-tone-wait=[>1](maximum number of frames waiting on each tone){default=90}\n"; 00696 return com ; 00697 } 00698 00699 extern "C" int main(const int argc, char** argv) 00700 { 00701 00702 MYLOGVERB = LOG_INFO; // suppress debug messages 00703 //let's push the initial value for the parameters 00704 argMap["experiment"]="spatial-memory-test"; 00705 argMap["logfile"]="psycho-sm.psy" ; 00706 argMap["string-size"]="3" ; 00707 argMap["num-of-trials"]="10"; 00708 argMap["subject"]="" ; 00709 argMap["memo"]="" ; 00710 argMap["single-dot-onset"]="16" ; 00711 argMap["dots-ISI"]="16"; 00712 argMap["spatial-delay"]="180"; 00713 argMap["spatial-counter-ISI"]="16"; 00714 argMap["counter-length"]="460"; 00715 argMap["counter-spatial-query-ISI"]="16"; 00716 argMap["spatial-query-length"]="60"; 00717 argMap["spatial-query-counter-query-ISI"]="30"; 00718 argMap["counter-query-length"]="60"; 00719 argMap["alphabet"]="0123456789"; 00720 argMap["mode"]="1" ; 00721 argMap["cue-onset-frames"] = "3" ; 00722 argMap["sound-dir"]=".."; 00723 argMap["tone1"]="sine"; 00724 argMap["tone2"]="square"; 00725 argMap["cue-file"]="cue1"; 00726 argMap["min-tone-wait-1"]="40"; 00727 argMap["max-tone-wait-1"]="60"; 00728 argMap["min-tone-wait-2"]="90"; 00729 argMap["max-tone-wait-2"]="110"; 00730 argMap["dots_radius"]="100"; 00731 argMap["dots_min_angle"]="45"; 00732 argMap["dots_max_angle"]="90"; 00733 manager.addSubComponent(d); 00734 nub::soft_ref<EventLog> el(new EventLog(manager)); 00735 manager.addSubComponent(el); 00736 d->setEventLog(el); 00737 nub::soft_ref<EyeTrackerConfigurator> 00738 etc(new EyeTrackerConfigurator(manager)); 00739 manager.addSubComponent(etc); 00740 00741 if (manager.parseCommandLine(argc, argv, 00742 "at least one argument needed", 1, -1)==false){ 00743 cout<<getUsageComment()<<endl; 00744 return(1); 00745 } 00746 00747 for(uint i = 0 ; i < manager.numExtraArgs() ; i++){ 00748 addArgument(manager.getExtraArg(i),std::string("=")) ; 00749 } 00750 00751 manager.setOptionValString(&OPT_EventLogFileName, argMap["logfile"]); 00752 //+manager.setOptionValString(&OPT_EyeTrackerType, "ISCAN"); 00753 //+nub::soft_ref<EyeTracker> eyet = etc->getET(); 00754 //+d->setEyeTracker(eyet); 00755 //+eyet->setEventLog(el); 00756 00757 00758 // let's get all our ModelComponent instances started: 00759 manager.start(); 00760 for(map<string,string>::iterator it= argMap.begin(); it!= argMap.end() ; ++it) d->pushEvent("arg:"+ it->first+" value:"+it->second ) ; 00761 // let's display an ISCAN calibration grid: 00762 d->clearScreen(); 00763 d->displayISCANcalib(); 00764 d->waitForMouseClick(); 00765 d->displayText("Here the experiment starts! click to start!"); 00766 d->waitForMouseClick(); 00767 d->clearScreen(); 00768 //let's see in what mode the user like to run the program 00769 //int mode = atoi(argMap["mode"].c_str()); 00770 string masterString=argMap["alphabet"]; 00771 00772 00773 int numOfTests = atoi(argMap["num-of-trials"].c_str()) ; 00774 int stringSize = atoi(argMap["string-size"].c_str()); 00775 int dots_ISI = atoi(argMap["dots-ISI"].c_str()) ; 00776 int dot_onset = atoi(argMap["single-dot-onset"].c_str()); 00777 int dots_radius = atoi(argMap["dots_radius"].c_str()); 00778 int dots_min_angle = atoi(argMap["dots_min_angle"].c_str()); 00779 int dots_max_angle = atoi(argMap["dots_max_angle"].c_str()); 00780 int mode = atoi(argMap["mode"].c_str()); 00781 int counter_length = atoi(argMap["counter-length"].c_str()); 00782 int min_tone_wait_1 = atoi(argMap["min-tone-wait-1"].c_str()); 00783 int max_tone_wait_1 = atoi(argMap["max-tone-wait-1"].c_str()); 00784 int min_tone_wait_2 = atoi(argMap["min-tone-wait-2"].c_str()); 00785 int max_tone_wait_2 = atoi(argMap["max-tone-wait-2"].c_str()); 00786 //let's do calibration 00787 d->displayText("CLICK LEFT button to calibrate; RIGHT to skip"); 00788 int cl = d->waitForMouseClick(); 00789 if (cl == 1) d->displayEyeTrackerCalibration(3,5,1 , true); 00790 d->clearScreen(); 00791 00792 00793 Mix_Music* cueMusic = NULL;//this is the cue for the start of trial 00794 Mix_Music* tone1 = NULL;//tone 1 00795 Mix_Music* tone2 = NULL;//tone 2 00796 map<int,Mix_Music*> audio_map ;//we will have access to the audios for stimulus presentation using this map 00797 map<string,int> charmap ; 00798 map<int,string> charinvmap ; 00799 //now let's open the audio channel 00800 if( Mix_OpenAudio( 22050, MIX_DEFAULT_FORMAT, 2, 4096 ) == -1 ){ 00801 LINFO( "did not open the mix-audio") ; 00802 return -1 ; 00803 } 00804 //now that everyting is ok, let's load the main string audio files and push them in a map 00805 for(uint i = 0; i < masterString.size() ; i++){ 00806 string str = argMap["sound-dir"]+"/"+masterString.substr(i,1)+".wav"; 00807 audio_map[i]=Mix_LoadMUS(str.c_str()); 00808 charmap[masterString.substr(i,1)]=i; 00809 charinvmap[i]=masterString.substr(i,1) ; 00810 } 00811 00812 for( uint i=10;i<100 ;i++ ){ 00813 charmap[stringify(i)]=i; 00814 charinvmap[i]=stringify(i); 00815 } 00816 00817 if(argMap["cue-type"].compare("a")==0){ 00818 string str = argMap["sound-dir"]+"/"+argMap["cue-file"]+".wav" ; 00819 cueMusic = Mix_LoadMUS(str.c_str()); 00820 } 00821 00822 string tmpstr = argMap["sound-dir"]+"/"+argMap["tone1"]+".wav"; 00823 tone1 = Mix_LoadMUS(tmpstr.c_str()); 00824 tmpstr = argMap["sound-dir"]+"/"+argMap["tone2"]+".wav"; 00825 tone2 = Mix_LoadMUS(tmpstr.c_str()); 00826 00827 int cr = numOfTests ;//this is the counter for number of rounds 00828 d->showCursor(true); 00829 d->displayText("click one of the mouse buttons to start!"); 00830 d->waitForMouseClick() ; 00831 d->showCursor(false); 00832 00833 int correctSpatialMemory_1=0; 00834 int incorrectSpatialMemory_1=0; 00835 int correctSpatialMemory_2=0; 00836 int incorrectSpatialMemory_2=0; 00837 int correctCounting=0; 00838 int incorrectCounting=0; 00839 int missCounting=0; 00840 vector<int> tempoVector; 00841 for( int i = 0 ; i < numOfTests /2 ;i++ ){ 00842 tempoVector.push_back(0); 00843 } 00844 for( int i = numOfTests /2 ; i < numOfTests ;i++ ){ 00845 tempoVector.push_back(1); 00846 } 00847 scramble(tempoVector); 00848 cr=0; 00849 while( cr < numOfTests ){ 00850 string inStr = getARandomString(stringSize,"234567"); 00851 d->showCursor(false); 00852 d->clearScreen() ; 00853 d->pushEvent("**************************************") ; 00854 if(tempoVector.at(cr)==0) d->pushEvent("fast tempo"); 00855 if(tempoVector.at(cr)==1) d->pushEvent("slow tempo"); 00856 d->pushEvent("initial string :" + inStr); 00857 for(uint i = 0 ; i < inStr.size(); i++){ 00858 d->pushEvent("playing character "+inStr.substr(i,1)); 00859 if(Mix_PlayMusic(audio_map[charmap[inStr.substr(i,1)]],0)==-1){return 1;} 00860 while(Mix_PlayingMusic()==1){} ; 00861 d->pushEvent("ended playing character "+inStr.substr(i,1)); 00862 } 00863 d->waitFrames(60); 00864 d->displayFixation(); 00865 d->waitFrames(10); 00866 d->clearScreen() ; 00867 vector<int> angles = spatial_memory_task(dot_onset,dots_ISI,dots_radius,dots_min_angle,dots_max_angle); 00868 d->pushEvent("dots showed at these angles :"+ stringify(angles.at(0))+" and " + stringify(angles.at(1))); 00869 d->waitFrames(10); 00870 d->displayFixation(); 00871 vector<int> tonesVector; 00872 if(tempoVector.at(cr)==0) 00873 tonesVector = executive_memory_task( tone1 , tone2 , inStr, counter_length, min_tone_wait_1, max_tone_wait_1 ); 00874 if(tempoVector.at(cr)==1) 00875 tonesVector = executive_memory_task( tone1 , tone2 , inStr, counter_length, min_tone_wait_2, max_tone_wait_2 ); 00876 string newStr ; 00877 if (stringSize == 2) 00878 newStr = charinvmap[charmap[inStr.substr(0,1)]+tonesVector.at(0)]+charinvmap[charmap[inStr.substr(inStr.size()-1,1)]+tonesVector.at(1)]; 00879 if (stringSize>2) 00880 newStr = charinvmap[charmap[inStr.substr(0,1)]+tonesVector.at(0)]+inStr.substr(1,inStr.size()-2)+charinvmap[charmap[inStr.substr(inStr.size()-1,1)]+tonesVector.at(1)]; 00881 if(mode==1 || mode==3){ 00882 int spMemAns = spatial_memory_retrival(angles,dots_radius); 00883 if(tempoVector.at(cr)==0){ 00884 switch( spMemAns ){ 00885 case 0 : incorrectSpatialMemory_1++;d->pushEvent("incorrect spatial memory recall");break; 00886 case 1 : correctSpatialMemory_1++;d->pushEvent("correct spatial memory recall");break; 00887 } 00888 } 00889 00890 if(tempoVector.at(cr)==1){ 00891 switch( spMemAns ){ 00892 case 0 : incorrectSpatialMemory_2++;d->pushEvent("incorrect spatial memory recall");break; 00893 case 1 : correctSpatialMemory_2++;d->pushEvent("correct spatial memory recall");break; 00894 } 00895 } 00896 } 00897 00898 00899 if(mode ==1){ 00900 //string ans = getDigitSequenceFromSubject(masterString,3," 3 digits"); 00901 string ans1 = getDigitSequenceFromSubject(masterString,1,"first digit"); 00902 d->pushEvent("subject keyed:" + ans1 + " for first digit"); 00903 string ans2 = getDigitSequenceFromSubject(masterString,1,"second digit"); 00904 d->pushEvent("subject keyed:" + ans2 + " for second digit"); 00905 //d->pushEvent("subject keyed:" + ans); 00906 if(ans1.compare(inStr.substr(0,1))==0 && ans2.compare(inStr.substr(inStr.size()-1,1))==0 ) { 00907 correctCounting++; 00908 }else{ 00909 incorrectCounting++ ; 00910 } 00911 00912 } 00913 00914 if(mode ==2 || mode==3){ 00915 int sineCounter = tonesVector.at(0)+atoi(inStr.substr(0,1).c_str()); 00916 int squareCounter = tonesVector.at(1)+atoi(inStr.substr(inStr.size()-1,1).c_str()); 00917 d->pushEvent("sine signal counter is :"+stringify(sineCounter)); 00918 d->pushEvent("square signal counter is :"+stringify(squareCounter)); 00919 string ans1 = getDigitSequenceFromSubject(masterString,2,"soft tone counter"); 00920 d->pushEvent("subject keyed:" + ans1 + " for sine counter"); 00921 string ans2 = getDigitSequenceFromSubject(masterString,2,"rough tone counter"); 00922 d->pushEvent("subject keyed:" + ans2 + " for square counter"); 00923 d->pushEvent("trial misscounting: " + stringify(abs(atoi(ans1.c_str()) - sineCounter) + abs(atoi(ans2.c_str()) - squareCounter))); 00924 missCounting += abs(atoi(ans1.c_str()) - sineCounter) + abs(atoi(ans2.c_str()) - squareCounter); 00925 00926 } 00927 cr++; 00928 } 00929 d->pushEvent("correct spatial memory (fast tempo): " +stringify(correctSpatialMemory_1)); 00930 d->pushEvent("incorrect spatial memory (fast tempo): " +stringify(incorrectSpatialMemory_1)); 00931 d->pushEvent("correct spatial memory (slow tempo): " +stringify(correctSpatialMemory_2)); 00932 d->pushEvent("incorrect spatial memory (slow tempo): " +stringify(incorrectSpatialMemory_2)); 00933 d->pushEvent("correct counting: " +stringify(correctCounting)); 00934 d->pushEvent("incorrect counting: " +stringify(incorrectCounting)); 00935 d->pushEvent("total misscounting: "+stringify(missCounting)); 00936 d->displayText("Experiment complete. Thank you!"); 00937 d->waitForMouseClick(); 00938 00939 // stop all our ModelComponents 00940 manager.stop(); 00941 00942 00943 // all done! 00944 return 0; 00945 } 00946 00947 #endif // INVT_HAVE_LIBSDL_IMAGE 00948