From 43bfe4466abdd10fe354cca26a465fc3cd54413d Mon Sep 17 00:00:00 2001 From: Thomas Pietrzak Date: Wed, 15 Jun 2011 21:19:28 +0000 Subject: [PATCH] JND exp functionnal git-svn-id: svn+ssh://thomaspietrzak.com/var/svn/rep@34 47cf9a05-e0a8-4ed5-9e9b-101a649bc004 --- Tactons Experiment.sln | 6 - Tactons Experiment.suo | Bin 28160 -> 36864 bytes Tactons JND/Tactons JND.vcxproj | 1 + Tactons JND/answer3AFC.cpp | 231 +++++++++++++++++++++++++++++--- Tactons JND/answer3AFC.h | 29 +++- Tactons JND/main.cpp | 2 + Tactons JND/tactonsjnd.cpp | 11 ++ Tactons JND/tactonsjnd.ui | 152 ++++++++++++--------- 8 files changed, 333 insertions(+), 99 deletions(-) diff --git a/Tactons Experiment.sln b/Tactons Experiment.sln index 4f417b4..2f5c058 100644 --- a/Tactons Experiment.sln +++ b/Tactons Experiment.sln @@ -5,8 +5,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Tactons Experiment", "Tacto EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Tactons JND", "Tactons JND\Tactons JND.vcxproj", "{454A6651-24D2-4DBA-A579-FDACD5CAC68C}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TactonPlayer", "TactonPlayer\TactonPlayer.vcxproj", "{6D5C62C0-562D-4B2A-9AAC-806372810CBF}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -21,10 +19,6 @@ Global {454A6651-24D2-4DBA-A579-FDACD5CAC68C}.Debug|Win32.Build.0 = Debug|Win32 {454A6651-24D2-4DBA-A579-FDACD5CAC68C}.Release|Win32.ActiveCfg = Release|Win32 {454A6651-24D2-4DBA-A579-FDACD5CAC68C}.Release|Win32.Build.0 = Release|Win32 - {6D5C62C0-562D-4B2A-9AAC-806372810CBF}.Debug|Win32.ActiveCfg = Debug|Win32 - {6D5C62C0-562D-4B2A-9AAC-806372810CBF}.Debug|Win32.Build.0 = Debug|Win32 - {6D5C62C0-562D-4B2A-9AAC-806372810CBF}.Release|Win32.ActiveCfg = Release|Win32 - {6D5C62C0-562D-4B2A-9AAC-806372810CBF}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Tactons Experiment.suo b/Tactons Experiment.suo index a4343e8558752c71f06b906e5857ce2dbb38cdac..3e7a78110d13201880a35ba7a88bc766aea0dc3c 100644 GIT binary patch delta 4779 zcmc&&dr*^C7XNPi5)uqT03jfe5CRSIFo`_nVSw-ud5cvkA|aqcc~}T4R!elPbzE$1 zPCIRPosQbBYg>n5jgNMS|KB54z2w_9m5k-hh z1nHC@Ek$I3r6}zAmKz?vlFfrSK2}k??ZVLm$A_kauZ7houZg!fJ5;}Z538f*9k}y5sx62B32>hA(|0XPcS1tA8`m_L0AzB5b21& zAhHnfv0?@52TMRC8-m3F$t$SgYOEmYK-wztZAjZCel5~<65omRaYUC??m^m%STB|P zBnp=TQh4gr`*XlW#?)|LHKC-L%tVMw4XlwfJ(bWJ@&IK134=2$R7{8LT#}V2g2kbS9|J)CR1$ z5=b+F09}s9VDq|lE~dyu!9FeAnY)=K!$9m1OMzRlHA?b@2+}2|ap`MR++r-n`XoQ- zYoItdAKY=}a4{ie=A?CkCSK=lGK4Y>`OkVZ;6~1b{$l3R;Qjaovs@0RtGB0p)B5(l zj_!7F$J^jcVl>1gCVGtJWHhhKILs8*LaTM_J7}s7_2d?w#2ENAF^L_gnz+d|-R(WD zrUs$&etku?y{X&L(F0GJQlKoc^O1*Yik1DHJ#D0)cYxU!n(^<+LThPBR(gpgwJ0ln zL8`@KH>VbsSPE0k=0bCEj=8k7w0ObREqPTP{m#;kuJ-*{FjbMy^* z9O1$&HYEiy+WH6AQ-RS3plRu||L^CDL$IA*+x#L1YHI-#y}u=@ z84gyJvX`fIM_X<*G*_AA6h9BsSQjvttH-GChw&;Md{AXjQ)~}#ecX8P#92&aT8N3! zQUD8|g-ZoyE{39-Xzn<`FwUD0!+v@&M>^4w*K@h0!v~%#JbtF|le~Rj?wzlg zMg0V)VV4npWHvGH(gx7?v!*ywx zeVe#`(OC2wKDJjX*ac7f#a=JDRbYm%xu3wBw+Q%@5Cv(8BRmkBW$;aMM8wllFGThr zWI`DnrVOaC+H{mFwxj4*2;xb7r^KHGUG}e;7v9K@AELVBC_f?9y)97`Pn<&jG~(S! zGLqBxCzPEIzvfEVVWcXS4oW=^ETE~O8%ilEn|nLT>^#R)hr{d%a8Gn zmG6@A7}QdyODfr{dnOSWsLrtZ4OCi^7G|w9#t-8aX(3L18G1K`X}=H&6dA2pav%ob zW=*oRe9J38RSl17=QdY0R#pG5c1xHLTb+-YG}9U;xDUkzL$ghUpjwnsz?pKd|`Pb0bMqVD;oW5armx9LkWEz6jp(Q9M z?#AW#-~SUU(?PF}X0J~>5z)MfFmI6&_GVnjstbXJ0%Oo7Oe4PBR*ODlwyecT z_oC?>NSnAPxXEQoFQ@+YNyN2@&wt-Lke?AyHq_#Qmf~>CwUmDPIXzeIOHpxM*vBBs z2hbqYa_h6NzqR~7){8lhhMv7Ry1Ya~i3wyOp{&x^CgJ`zVy+xF7ApEq{vqk>PgV?M zU3sJIMQRtEj(uh!#I)9gJ=jv6I2x^|4S##lZT5I7PZ)P2EtF;==@)mG8 z8+dw@P_?QX8Onzo+6iAA>0W!=X%-~zz4$=b?dL|2E~hKD2WtnQF*gEsE&P_fIjydg zKvcd{G!BBwUt(fQ%yF}P@<{Bjn)A7pW{O*R7j+#r8zr<`cIdGX{esUTxq@NKDU* z^T-?%#D)?BU+NQIL}ozjq`6-HA(pzu(h`HR97}dm#L>boA@c-+zDXJGo>5ZZ=EB@5 z+e;U($tI$=NhL14tRQ@{*K-`bFDiT%(MH~HJ4#dZ?juj?hHtGAu&-7PwUrV0W};-i zUy#?&h1$kW@0)9P%AmQ!1bq2^*}Wam>ipkqIL-k7 zsGGd9R5%l}2*&F3AgvB(u{Z+eW+-IMG!?FR#C3xd9K9va(G4&!Sx|vB%uaf_l z)5hFyc%M1%^H*)$L#;)Y_Zvo+>Oxu41NQV?;OU|~m&L+?@>u8zYJkyI1-QTT==5g| z@~vpO9yRDv@9-oTz)%vW65jcRC>6rQiOh%?J?PQ2r5_>!LD_whc>%#GpgPGA!YW zM8z}6d?+d|5qrIil5L{0Gm)?GDPBXSR}^>E%b&s0A*fR>0eg2Ij8&>3ZLtADN|i8L zt6%;?9@`FsW6ef%++(n#{#p2E_`N*B_2Hv@$tk(UmYIGh6F1MyX=19h{AbH0Z? ze{C@fF~)3(Gsd$gIj8to=9X>IB}+Cp-Ar%~ver9yOw;hB6Z5E zlo}}Fg32MemKiBk;b3NJQjDvcgN{djzke+1Y%mTi0WOdOJ^>wo+i(n!_knz10^>me zCr zU?q^iaX^jn8*7k+H+svoJBpY0ka4z(dPnDt zv?lc@cQUq@x>Am-e9etZ^)qv(zPsPkLRPhhsfl%#kWZ~8ZR)6yhO6P!9Xp9fTH2Vp zf+_fGevhW%F>6>Ztx8+Z^z<;TRtYEliGjD1TT>gxc(~GnnjuZCobe#?DUmz9UgS&0 zT@^<{YDul2H!{?cIwOwJzVtZqWLn9ZX`v78aNb%k%}NGWOAJjgmncZGz$QGmN3jG16DsNXBElI54+p`X)1*&zH%muc$qs ztw~g2S%M`U0uPS!KOdyI=0w_26jQ*zU+42!smadZn|U1_!1)}OkNfK6*&$dq1O(@^ zt?Tsg@Zg97Z7)xY%Uj*jxx%%)ZJlF6EtOaR^1oKMuht=+WY` z6HC&N<<$*MjL;ZOgeBTdh;?p!j8M0^olYBGfw<#-w8wE|F!n&Q&_`B-maLugSskVC$fwCW zqo~9YBQlxo?=KBW&Vnd?77djK2;N5E0=F*pWz zw&9!z$lMVhI0a6FGvF*Z2hM{F;3D`8Tmqki%is%e1zZJ#;2O9NZt(Pc69Uy0YgFGs z_#Vh{4A8pbEX~ho+y%b??#4aT_XF}DsL5A6D(p`Pe{m(Ijj)0Js)1UlCE_7gp9t_fq<5Pr)Oqn3Iexa!}a;5&;K zhQP=`2oM3^LJHKOKncP?IEVl$5DB6{G>8F@B-{LxjQYB?bh|p4hcR|Fu1cM7--Rns zn-D342Ll}cbdP4FuRHc7+7qe4a#c!+?oBev7Gor98|GMJH<3|{^0qtp9f;iRk8eiU cBf??aAWG1wB43q?2DccgH`YiRWkRt1FJ}m}0ssI2 diff --git a/Tactons JND/Tactons JND.vcxproj b/Tactons JND/Tactons JND.vcxproj index 83c587d..8fb9f8c 100644 --- a/Tactons JND/Tactons JND.vcxproj +++ b/Tactons JND/Tactons JND.vcxproj @@ -50,6 +50,7 @@ ProgramDatabase MultiThreadedDebugDLL false + Level3 Windows diff --git a/Tactons JND/answer3AFC.cpp b/Tactons JND/answer3AFC.cpp index 58ea3b1..34cbc13 100644 --- a/Tactons JND/answer3AFC.cpp +++ b/Tactons JND/answer3AFC.cpp @@ -1,58 +1,249 @@ #include "answer3AFC.h" #include - #include +#include +#include + +#define RESETVAL (3) Answer3AFC::Answer3AFC(QWidget *parent, Qt::WFlags flags) - : QDialog(parent, flags), _logfile(NULL), _tactonPlayer("COM3"), _current(0) +:QDialog(parent, flags), +_logfile(NULL), _tactonPlayer("COM3"), _defaulttacton(0x0f, 50, 205, 100), +_currenttest(0.0), _goodanswer(-1), _block(0) { //init the window setupUi(this); - this->layout()->setSizeConstraint(QLayout::SetFixedSize); +// this->layout()->setSizeConstraint(QLayout::SetFixedSize); + + for (int i = 0 ; i < 3 ; i++) + _tplayed[i] = 0; //init buttons - QObject::connect(play1, SIGNAL(clicked()), this, SLOT(play(1))); - QObject::connect(play2, SIGNAL(clicked()), this, SLOT(play(2))); - QObject::connect(play3, SIGNAL(clicked()), this, SLOT(play(3))); - QObject::connect(answer1, SIGNAL(clicked()), this, SLOT(answer(1))); - QObject::connect(answer2, SIGNAL(clicked()), this, SLOT(answer(2))); - QObject::connect(answer3, SIGNAL(clicked()), this, SLOT(answer(3))); + QObject::connect(play1, SIGNAL(clicked()), this, SLOT(play())); + QObject::connect(play2, SIGNAL(clicked()), this, SLOT(play())); + QObject::connect(play3, SIGNAL(clicked()), this, SLOT(play())); + QObject::connect(answer1, SIGNAL(clicked()), this, SLOT(answer())); + QObject::connect(answer2, SIGNAL(clicked()), this, SLOT(answer())); + QObject::connect(answer3, SIGNAL(clicked()), this, SLOT(answer())); +} + +Answer3AFC::~Answer3AFC() +{ + if (_logfile) + fclose(_logfile); + //reset the wristband + _tactonPlayer.stop(); +} + +void Answer3AFC::init(QString user, int repetitions, int reversals, double tinc, double finc, int tseries, int fseries, QString experiment, QString gesture) +{ + _user = user; + _repetitions = repetitions; + _reversals = reversals; + _tinc = tinc; + _finc = finc; + _tseries = tseries; + _fseries = fseries; + _experiment = experiment; + _gesture = gesture; + + //create log file + QString logfilename = user + "-" + experiment + "-" + gesture + "-"; + _logfile = fopen(logfilename.toStdString().c_str(), "w"); + + //load icons + FILE *f = fopen((experiment + ".txt").toAscii().data(), "r"); + if (f) + { + char buffer[256]; + while ((fgets(buffer, 256, f)) != NULL) + { + unsigned int val = atoi(buffer); + _values.push_back(val); + for (int i = 0 ; i < _repetitions ; i++) + _trials.push_back(val); + } + fclose(f); + //shuffle + for (int i = 0; i < _trials.count(); i++) + _trials.swap(i, (rand() % (_trials.count() - i)) + i); + _jnd.resize(_trials.count()); +/* _jnd = new double[_trials.count()]; + memset(_jnd, 0, _trials.count() * sizeof(double));*/ + } + + //init the wristband + //_tactonPlayer.start(); + + runBlock(); } void Answer3AFC::toggleButtons() { bool ok = _tplayed[0] && _tplayed[1] && _tplayed[2]; - play1->setEnabled(ok); - play2->setEnabled(ok); - play3->setEnabled(ok); + answer1->setEnabled(ok); + answer2->setEnabled(ok); + answer3->setEnabled(ok); } -void Answer3AFC::play(int tacton) +void Answer3AFC::play() { + QPushButton *button = (QPushButton *)sender(); + int tacton; + if (button->objectName() == "play1") + tacton = 0; + else if (button->objectName() == "play2") + tacton = 1; + else if (button->objectName() == "play3") + tacton = 2; + + unsigned int value = 0; if (tacton == _goodanswer) - _tactonPlayer.play(Tacton( - - (...[tacton]); + value = _trials[_block]; + else + value = testValue(_trials[_block], _currenttest); + + Tacton t(_defaulttacton); + if (_experiment == "Frequencies") + t.setFrequency(value); + else if (_experiment == "Amplitudes") + t.setAmplitude(value); + else if (_experiment == "Duration") + t.setDuration(value); + _tactonPlayer.play(t); + _tplayed[tacton]++; toggleButtons(); } -void Answer3AFC::answer(int rep) +void Answer3AFC::answer() { + QPushButton *button = (QPushButton *)sender(); + int rep; + if (button->objectName() == "answer1") + rep = 0; + else if (button->objectName() == "answer2") + rep = 1; + else if (button->objectName() == "answer3") + rep = 2; + log(rep); _tplayed[0] = _tplayed[1] = _tplayed[2] = 0; toggleButtons(); + static int nb = 0, rev = 0; + + static bool newblock = true; + static bool lastval; + bool val = rep == _goodanswer; + + //reversal + if (!newblock && val != lastval) + { + nb = 1; + rev++; + _jnd[_block] += _currenttest / _reversals; + //if enough reversals we switch to a new block + if (rev >= _reversals) + { + log2(); + _block++; + nb = 0; + rev = 0; + newblock = true; + runBlock(); + } + else + { + if (val) + { + if (_tseries == 1 && _currenttest >= _tinc) + { + //change + _currenttest -= _tinc; + nb = 0; + } + } + //wrong answer: increase the gap + else + { + if (_fseries == 1)// && _currenttest < RESETVAL) + { + //change + _currenttest += _finc; + nb = 0; + } + } + } + } + //series + else + { + newblock = false; + nb++; + //good answer: reduce the gap + if (val) + { + if (nb >= _tseries && _currenttest >= _tinc) + { + //change + _currenttest -= _tinc; + nb = 0; + } + } + //wrong answer: increase the gap + else + { + if (nb >= _fseries)// && _currenttest < RESETVAL) + { + //change + _currenttest += _finc; + nb = 0; + } + } + } + lastval = val; + runTrial(); } -void Answer3AFC::log(bool answer) +void Answer3AFC::log(int answer) const +{ + if (_logfile == NULL) + return; + fprintf(_logfile, "ANSWER,%d,%d,%0.2f,%d,%d,%d,%d,%d\n", _block, _trials[_block], _currenttest, _goodanswer, answer, _tplayed[0], _tplayed[1], _tplayed[2]); +} + +void Answer3AFC::log2() const { if (_logfile == NULL) return; - fprintf(_logfile, "%d,%d,%d,%d,%d,%d,%d\n", _currentval, _currenttest, _goodanswer, answer, _tplayed[0], _tplayed[1], _tplayed[2]); + fprintf(_logfile, "JND,%d,%d,%0.2f\n", _block, _trials[_block], _jnd[_block]); +} + +void Answer3AFC::runBlock() +{ + if (_block >= _trials.count()) + { + play1->setEnabled(false); + play2->setEnabled(false); + play3->setEnabled(false); + answer1->setEnabled(false); + answer2->setEnabled(false); + answer3->setEnabled(false); + label->setText("Finished!"); + } + + _currenttest = RESETVAL; + runTrial(); } - + void Answer3AFC::runTrial() { + _goodanswer = rand() % 3; +} + +unsigned int Answer3AFC::testValue(unsigned int value, double db) const +{ + return static_cast(value / pow(10.0, db / 10.0)); } \ No newline at end of file diff --git a/Tactons JND/answer3AFC.h b/Tactons JND/answer3AFC.h index 58ffd2a..a7c711f 100644 --- a/Tactons JND/answer3AFC.h +++ b/Tactons JND/answer3AFC.h @@ -1,6 +1,7 @@ #ifndef _ANSWER3AFC_ #define _ANSWER3AFC_ +#include #include #include @@ -15,30 +16,44 @@ public: Answer3AFC(QWidget *parent = 0, Qt::WFlags flags = 0); ~Answer3AFC(); - void init(QString user, int repetitions, QString experiment, QString gesture); + void init(QString user, int repetitions, int reversals, double tinc, double finc, int tseries, int fseries, QString experiment, QString gesture); void setAnswer(bool same); private: - void log(bool answer); + void log(int answer) const; + void log2() const; void runTrial(); + void runBlock(); + unsigned int testValue(unsigned int value, double db) const; FILE *_logfile; TactonPlayer _tactonPlayer; + Tacton _defaulttacton; + + //exp parameters QString _user; - int _reversals, tseries, _fseries; + int _repetitions, _reversals, _tseries, _fseries; double _tinc, _finc; QString _experiment; QString _gesture; - int _currentval, _currenttest, _goodanswer; - + //exp data + QList _values; + QList _trials; + + //exp status + int _goodanswer, _block; + double _currenttest; + + //results + QVector _jnd; int _tplayed[3]; private slots: void toggleButtons(); - void play(int tacton); - void answer(int rep); + void play(); + void answer(); }; #endif diff --git a/Tactons JND/main.cpp b/Tactons JND/main.cpp index d0c1492..d014216 100644 --- a/Tactons JND/main.cpp +++ b/Tactons JND/main.cpp @@ -1,8 +1,10 @@ #include "tactonsjnd.h" #include +#include int main(int argc, char *argv[]) { + srand(time(NULL)); QApplication a(argc, argv); TactonsJND w; w.show(); diff --git a/Tactons JND/tactonsjnd.cpp b/Tactons JND/tactonsjnd.cpp index 6fedb8c..7274998 100644 --- a/Tactons JND/tactonsjnd.cpp +++ b/Tactons JND/tactonsjnd.cpp @@ -1,5 +1,7 @@ #include "tactonsjnd.h" +#include + TactonsJND::TactonsJND(QWidget *parent, Qt::WFlags flags) : QMainWindow(parent, flags) { @@ -40,3 +42,12 @@ TactonsJND::~TactonsJND() { } + +void TactonsJND::runBlock(void) +{ + this->hide(); + w.init(username->text(), repetitions->value(), + reversals->value(), tinc->value(), finc->value(), tseries->value(), fseries->value(), + experiment->currentText(), gesture->currentText()); + w.show(); +} diff --git a/Tactons JND/tactonsjnd.ui b/Tactons JND/tactonsjnd.ui index 35226d9..7b97944 100644 --- a/Tactons JND/tactonsjnd.ui +++ b/Tactons JND/tactonsjnd.ui @@ -6,8 +6,8 @@ 0 0 - 461 - 270 + 231 + 279 @@ -20,8 +20,8 @@ - - + + @@ -32,14 +32,34 @@ + + + + 1 + + + 50 + + + 1 + + + + + Repetitions: + + + + + Reversals: - + 2 @@ -52,55 +72,78 @@ - - + + - Experiment: + T Inc - - - - - - - Gesture: + + + + 10.000000000000000 - - - - - - - - - T Inc + + 0.250000000000000 + + + 1.000000000000000 - + F Inc - + + + + 0.000000000000000 + + + 10.000000000000000 + + + 0.250000000000000 + + + 1.000000000000000 + + + + T Series - + F Series - + + + + Experiment: + + + + + + + Gesture: + + + + 1 @@ -110,7 +153,7 @@ - + 1 @@ -120,49 +163,26 @@ - - - - 10.000000000000000 - - - 0.250000000000000 - - - 1.000000000000000 - - + + - - - - 0.000000000000000 - - - 10.000000000000000 - - - 0.250000000000000 - - - 1.000000000000000 - - + + - - - - - - - Start experiment - - - + + + + + + + Start experiment + + + -- 2.30.2