EndProject\r
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Tactons JND", "Tactons JND\Tactons JND.vcxproj", "{454A6651-24D2-4DBA-A579-FDACD5CAC68C}"\r
EndProject\r
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TactonPlayer", "TactonPlayer\TactonPlayer.vcxproj", "{6D5C62C0-562D-4B2A-9AAC-806372810CBF}"\r
-EndProject\r
Global\r
GlobalSection(SolutionConfigurationPlatforms) = preSolution\r
Debug|Win32 = Debug|Win32\r
{454A6651-24D2-4DBA-A579-FDACD5CAC68C}.Debug|Win32.Build.0 = Debug|Win32\r
{454A6651-24D2-4DBA-A579-FDACD5CAC68C}.Release|Win32.ActiveCfg = Release|Win32\r
{454A6651-24D2-4DBA-A579-FDACD5CAC68C}.Release|Win32.Build.0 = Release|Win32\r
- {6D5C62C0-562D-4B2A-9AAC-806372810CBF}.Debug|Win32.ActiveCfg = Debug|Win32\r
- {6D5C62C0-562D-4B2A-9AAC-806372810CBF}.Debug|Win32.Build.0 = Debug|Win32\r
- {6D5C62C0-562D-4B2A-9AAC-806372810CBF}.Release|Win32.ActiveCfg = Release|Win32\r
- {6D5C62C0-562D-4B2A-9AAC-806372810CBF}.Release|Win32.Build.0 = Release|Win32\r
EndGlobalSection\r
GlobalSection(SolutionProperties) = preSolution\r
HideSolutionNode = FALSE\r
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>\r
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>\r
<TreatWChar_tAsBuiltInType>false</TreatWChar_tAsBuiltInType>\r
+ <WarningLevel>Level3</WarningLevel>\r
</ClCompile>\r
<Link>\r
<SubSystem>Windows</SubSystem>\r
#include "answer3AFC.h"\r
\r
#include <QTimer>\r
- #include <QDebug>\r
+#include <QDebug>\r
+#include <cmath>\r
+\r
+#define RESETVAL (3)\r
\r
Answer3AFC::Answer3AFC(QWidget *parent, Qt::WFlags flags)\r
- : QDialog(parent, flags), _logfile(NULL), _tactonPlayer("COM3"), _current(0)\r
+:QDialog(parent, flags),\r
+_logfile(NULL), _tactonPlayer("COM3"), _defaulttacton(0x0f, 50, 205, 100), \r
+_currenttest(0.0), _goodanswer(-1), _block(0)\r
{\r
//init the window\r
setupUi(this);\r
- this->layout()->setSizeConstraint(QLayout::SetFixedSize);\r
+// this->layout()->setSizeConstraint(QLayout::SetFixedSize);\r
+\r
+ for (int i = 0 ; i < 3 ; i++)\r
+ _tplayed[i] = 0;\r
\r
//init buttons\r
- QObject::connect(play1, SIGNAL(clicked()), this, SLOT(play(1)));\r
- QObject::connect(play2, SIGNAL(clicked()), this, SLOT(play(2)));\r
- QObject::connect(play3, SIGNAL(clicked()), this, SLOT(play(3)));\r
- QObject::connect(answer1, SIGNAL(clicked()), this, SLOT(answer(1)));\r
- QObject::connect(answer2, SIGNAL(clicked()), this, SLOT(answer(2)));\r
- QObject::connect(answer3, SIGNAL(clicked()), this, SLOT(answer(3)));\r
+ QObject::connect(play1, SIGNAL(clicked()), this, SLOT(play()));\r
+ QObject::connect(play2, SIGNAL(clicked()), this, SLOT(play()));\r
+ QObject::connect(play3, SIGNAL(clicked()), this, SLOT(play()));\r
+ QObject::connect(answer1, SIGNAL(clicked()), this, SLOT(answer()));\r
+ QObject::connect(answer2, SIGNAL(clicked()), this, SLOT(answer()));\r
+ QObject::connect(answer3, SIGNAL(clicked()), this, SLOT(answer()));\r
+}\r
+\r
+Answer3AFC::~Answer3AFC()\r
+{\r
+ if (_logfile)\r
+ fclose(_logfile);\r
+ //reset the wristband\r
+ _tactonPlayer.stop();\r
+}\r
+\r
+void Answer3AFC::init(QString user, int repetitions, int reversals, double tinc, double finc, int tseries, int fseries, QString experiment, QString gesture)\r
+{\r
+ _user = user;\r
+ _repetitions = repetitions;\r
+ _reversals = reversals;\r
+ _tinc = tinc;\r
+ _finc = finc;\r
+ _tseries = tseries;\r
+ _fseries = fseries;\r
+ _experiment = experiment;\r
+ _gesture = gesture;\r
+\r
+ //create log file\r
+ QString logfilename = user + "-" + experiment + "-" + gesture + "-";\r
+ _logfile = fopen(logfilename.toStdString().c_str(), "w");\r
+\r
+ //load icons\r
+ FILE *f = fopen((experiment + ".txt").toAscii().data(), "r");\r
+ if (f)\r
+ {\r
+ char buffer[256];\r
+ while ((fgets(buffer, 256, f)) != NULL)\r
+ {\r
+ unsigned int val = atoi(buffer);\r
+ _values.push_back(val);\r
+ for (int i = 0 ; i < _repetitions ; i++)\r
+ _trials.push_back(val);\r
+ }\r
+ fclose(f);\r
+ //shuffle\r
+ for (int i = 0; i < _trials.count(); i++)\r
+ _trials.swap(i, (rand() % (_trials.count() - i)) + i);\r
+ _jnd.resize(_trials.count());\r
+/* _jnd = new double[_trials.count()];\r
+ memset(_jnd, 0, _trials.count() * sizeof(double));*/\r
+ }\r
+\r
+ //init the wristband\r
+ //_tactonPlayer.start();\r
+\r
+ runBlock();\r
}\r
\r
void Answer3AFC::toggleButtons()\r
{\r
bool ok = _tplayed[0] && _tplayed[1] && _tplayed[2];\r
- play1->setEnabled(ok);\r
- play2->setEnabled(ok);\r
- play3->setEnabled(ok);\r
+ answer1->setEnabled(ok);\r
+ answer2->setEnabled(ok);\r
+ answer3->setEnabled(ok);\r
}\r
\r
-void Answer3AFC::play(int tacton)\r
+void Answer3AFC::play()\r
{\r
+ QPushButton *button = (QPushButton *)sender();\r
+ int tacton;\r
+ if (button->objectName() == "play1")\r
+ tacton = 0;\r
+ else if (button->objectName() == "play2")\r
+ tacton = 1;\r
+ else if (button->objectName() == "play3")\r
+ tacton = 2;\r
+\r
+ unsigned int value = 0;\r
if (tacton == _goodanswer)\r
- _tactonPlayer.play(Tacton(\r
- \r
- (...[tacton]);\r
+ value = _trials[_block];\r
+ else\r
+ value = testValue(_trials[_block], _currenttest);\r
+\r
+ Tacton t(_defaulttacton);\r
+ if (_experiment == "Frequencies")\r
+ t.setFrequency(value);\r
+ else if (_experiment == "Amplitudes")\r
+ t.setAmplitude(value);\r
+ else if (_experiment == "Duration")\r
+ t.setDuration(value);\r
+ _tactonPlayer.play(t);\r
+\r
_tplayed[tacton]++;\r
toggleButtons();\r
}\r
\r
-void Answer3AFC::answer(int rep)\r
+void Answer3AFC::answer()\r
{\r
+ QPushButton *button = (QPushButton *)sender();\r
+ int rep;\r
+ if (button->objectName() == "answer1")\r
+ rep = 0;\r
+ else if (button->objectName() == "answer2")\r
+ rep = 1;\r
+ else if (button->objectName() == "answer3")\r
+ rep = 2;\r
+\r
log(rep);\r
_tplayed[0] = _tplayed[1] = _tplayed[2] = 0;\r
toggleButtons();\r
\r
+ static int nb = 0, rev = 0;\r
+\r
+ static bool newblock = true;\r
+ static bool lastval;\r
+ bool val = rep == _goodanswer;\r
+\r
+ //reversal\r
+ if (!newblock && val != lastval)\r
+ {\r
+ nb = 1;\r
+ rev++;\r
+ _jnd[_block] += _currenttest / _reversals;\r
+ //if enough reversals we switch to a new block\r
+ if (rev >= _reversals)\r
+ {\r
+ log2();\r
+ _block++;\r
+ nb = 0;\r
+ rev = 0;\r
+ newblock = true;\r
+ runBlock();\r
+ }\r
+ else\r
+ {\r
+ if (val)\r
+ {\r
+ if (_tseries == 1 && _currenttest >= _tinc)\r
+ {\r
+ //change \r
+ _currenttest -= _tinc;\r
+ nb = 0;\r
+ }\r
+ }\r
+ //wrong answer: increase the gap\r
+ else\r
+ {\r
+ if (_fseries == 1)// && _currenttest < RESETVAL)\r
+ {\r
+ //change \r
+ _currenttest += _finc;\r
+ nb = 0;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ //series\r
+ else\r
+ {\r
+ newblock = false;\r
+ nb++;\r
+ //good answer: reduce the gap\r
+ if (val)\r
+ {\r
+ if (nb >= _tseries && _currenttest >= _tinc)\r
+ {\r
+ //change \r
+ _currenttest -= _tinc;\r
+ nb = 0;\r
+ }\r
+ }\r
+ //wrong answer: increase the gap\r
+ else\r
+ {\r
+ if (nb >= _fseries)// && _currenttest < RESETVAL)\r
+ {\r
+ //change \r
+ _currenttest += _finc;\r
+ nb = 0;\r
+ }\r
+ }\r
+ }\r
+ lastval = val;\r
+\r
runTrial();\r
}\r
\r
-void Answer3AFC::log(bool answer)\r
+void Answer3AFC::log(int answer) const\r
+{\r
+ if (_logfile == NULL)\r
+ return;\r
+ 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]);\r
+}\r
+\r
+void Answer3AFC::log2() const\r
{\r
if (_logfile == NULL)\r
return;\r
- fprintf(_logfile, "%d,%d,%d,%d,%d,%d,%d\n", _currentval, _currenttest, _goodanswer, answer, _tplayed[0], _tplayed[1], _tplayed[2]);\r
+ fprintf(_logfile, "JND,%d,%d,%0.2f\n", _block, _trials[_block], _jnd[_block]);\r
+}\r
+\r
+void Answer3AFC::runBlock()\r
+{\r
+ if (_block >= _trials.count())\r
+ {\r
+ play1->setEnabled(false);\r
+ play2->setEnabled(false);\r
+ play3->setEnabled(false);\r
+ answer1->setEnabled(false);\r
+ answer2->setEnabled(false);\r
+ answer3->setEnabled(false);\r
+ label->setText("Finished!");\r
+ }\r
+\r
+ _currenttest = RESETVAL;\r
+ runTrial();\r
}\r
- \r
+\r
void Answer3AFC::runTrial()\r
{\r
+ _goodanswer = rand() % 3;\r
+}\r
+\r
+unsigned int Answer3AFC::testValue(unsigned int value, double db) const\r
+{\r
+ return static_cast<unsigned int>(value / pow(10.0, db / 10.0));\r
}
\ No newline at end of file
#ifndef _ANSWER3AFC_\r
#define _ANSWER3AFC_\r
\r
+#include <TactonPlayer/Tacton.hpp>\r
#include <TactonPlayer/TactonPlayer.hpp>\r
\r
#include <QtGui/QDialog>\r
Answer3AFC(QWidget *parent = 0, Qt::WFlags flags = 0);\r
~Answer3AFC();\r
\r
- void init(QString user, int repetitions, QString experiment, QString gesture);\r
+ void init(QString user, int repetitions, int reversals, double tinc, double finc, int tseries, int fseries, QString experiment, QString gesture);\r
\r
void setAnswer(bool same);\r
\r
private:\r
- void log(bool answer);\r
+ void log(int answer) const;\r
+ void log2() const;\r
void runTrial();\r
+ void runBlock();\r
+ unsigned int testValue(unsigned int value, double db) const;\r
\r
FILE *_logfile;\r
TactonPlayer _tactonPlayer;\r
+ Tacton _defaulttacton;\r
+\r
+ //exp parameters\r
QString _user;\r
- int _reversals, tseries, _fseries;\r
+ int _repetitions, _reversals, _tseries, _fseries;\r
double _tinc, _finc;\r
QString _experiment;\r
QString _gesture;\r
\r
- int _currentval, _currenttest, _goodanswer;\r
- \r
+ //exp data\r
+ QList<unsigned int> _values;\r
+ QList<unsigned int> _trials;\r
+\r
+ //exp status\r
+ int _goodanswer, _block;\r
+ double _currenttest;\r
+\r
+ //results\r
+ QVector<double> _jnd;\r
int _tplayed[3];\r
\r
private slots:\r
void toggleButtons();\r
- void play(int tacton);\r
- void answer(int rep);\r
+ void play();\r
+ void answer();\r
};\r
\r
#endif\r
#include "tactonsjnd.h"\r
#include <QtGui/QApplication>\r
+#include <ctime>\r
\r
int main(int argc, char *argv[])\r
{\r
+ srand(time(NULL));\r
QApplication a(argc, argv);\r
TactonsJND w;\r
w.show();\r
#include "tactonsjnd.h"\r
\r
+#include <QApplication>\r
+\r
TactonsJND::TactonsJND(QWidget *parent, Qt::WFlags flags)\r
: QMainWindow(parent, flags)\r
{\r
{\r
\r
}\r
+\r
+void TactonsJND::runBlock(void)\r
+{\r
+ this->hide();\r
+ w.init(username->text(), repetitions->value(), \r
+ reversals->value(), tinc->value(), finc->value(), tseries->value(), fseries->value(), \r
+ experiment->currentText(), gesture->currentText());\r
+ w.show();\r
+}\r
<rect>\r
<x>0</x>\r
<y>0</y>\r
- <width>461</width>\r
- <height>270</height>\r
+ <width>231</width>\r
+ <height>279</height>\r
</rect>\r
</property>\r
<property name="windowTitle">\r
<layout class="QGridLayout" name="gridLayout_3">\r
<item row="0" column="0">\r
<layout class="QGridLayout" name="gridLayout_2">\r
- <item row="0" column="0">\r
- <layout class="QGridLayout" name="gridLayout">\r
+ <item row="1" column="0">\r
+ <layout class="QGridLayout" name="gridLayout" rowstretch="0,0,0,0,0,0,0,0,0">\r
<item row="0" column="0">\r
<widget class="QLabel" name="labeluser">\r
<property name="text">\r
<item row="0" column="1">\r
<widget class="QLineEdit" name="username"/>\r
</item>\r
+ <item row="1" column="1">\r
+ <widget class="QSpinBox" name="repetitions">\r
+ <property name="minimum">\r
+ <number>1</number>\r
+ </property>\r
+ <property name="maximum">\r
+ <number>50</number>\r
+ </property>\r
+ <property name="value">\r
+ <number>1</number>\r
+ </property>\r
+ </widget>\r
+ </item>\r
<item row="1" column="0">\r
<widget class="QLabel" name="labelrepetitions">\r
+ <property name="text">\r
+ <string>Repetitions:</string>\r
+ </property>\r
+ </widget>\r
+ </item>\r
+ <item row="2" column="0">\r
+ <widget class="QLabel" name="labelreversals">\r
<property name="text">\r
<string>Reversals:</string>\r
</property>\r
</widget>\r
</item>\r
- <item row="1" column="1">\r
+ <item row="2" column="1">\r
<widget class="QSpinBox" name="reversals">\r
<property name="minimum">\r
<number>2</number>\r
</property>\r
</widget>\r
</item>\r
- <item row="6" column="0">\r
- <widget class="QLabel" name="labelexperiment">\r
+ <item row="3" column="0">\r
+ <widget class="QLabel" name="tinclabel">\r
<property name="text">\r
- <string>Experiment:</string>\r
+ <string>T Inc</string>\r
</property>\r
</widget>\r
</item>\r
- <item row="6" column="1">\r
- <widget class="QComboBox" name="experiment"/>\r
- </item>\r
- <item row="7" column="0">\r
- <widget class="QLabel" name="labelGesture">\r
- <property name="text">\r
- <string>Gesture:</string>\r
+ <item row="3" column="1">\r
+ <widget class="QDoubleSpinBox" name="tinc">\r
+ <property name="maximum">\r
+ <double>10.000000000000000</double>\r
</property>\r
- </widget>\r
- </item>\r
- <item row="7" column="1">\r
- <widget class="QComboBox" name="gesture"/>\r
- </item>\r
- <item row="2" column="0">\r
- <widget class="QLabel" name="tinclabel">\r
- <property name="text">\r
- <string>T Inc</string>\r
+ <property name="singleStep">\r
+ <double>0.250000000000000</double>\r
+ </property>\r
+ <property name="value">\r
+ <double>1.000000000000000</double>\r
</property>\r
</widget>\r
</item>\r
- <item row="3" column="0">\r
+ <item row="4" column="0">\r
<widget class="QLabel" name="finclabel">\r
<property name="text">\r
<string>F Inc</string>\r
</property>\r
</widget>\r
</item>\r
- <item row="4" column="0">\r
+ <item row="4" column="1">\r
+ <widget class="QDoubleSpinBox" name="finc">\r
+ <property name="minimum">\r
+ <double>0.000000000000000</double>\r
+ </property>\r
+ <property name="maximum">\r
+ <double>10.000000000000000</double>\r
+ </property>\r
+ <property name="singleStep">\r
+ <double>0.250000000000000</double>\r
+ </property>\r
+ <property name="value">\r
+ <double>1.000000000000000</double>\r
+ </property>\r
+ </widget>\r
+ </item>\r
+ <item row="5" column="0">\r
<widget class="QLabel" name="tserieslabel">\r
<property name="text">\r
<string>T Series</string>\r
</property>\r
</widget>\r
</item>\r
- <item row="5" column="0">\r
+ <item row="6" column="0">\r
<widget class="QLabel" name="fserieslabel">\r
<property name="text">\r
<string>F Series</string>\r
</property>\r
</widget>\r
</item>\r
- <item row="4" column="1">\r
+ <item row="7" column="0">\r
+ <widget class="QLabel" name="labelexperiment">\r
+ <property name="text">\r
+ <string>Experiment:</string>\r
+ </property>\r
+ </widget>\r
+ </item>\r
+ <item row="8" column="0">\r
+ <widget class="QLabel" name="labelgesture">\r
+ <property name="text">\r
+ <string>Gesture:</string>\r
+ </property>\r
+ </widget>\r
+ </item>\r
+ <item row="5" column="1">\r
<widget class="QSpinBox" name="tseries">\r
<property name="minimum">\r
<number>1</number>\r
</property>\r
</widget>\r
</item>\r
- <item row="5" column="1">\r
+ <item row="6" column="1">\r
<widget class="QSpinBox" name="fseries">\r
<property name="minimum">\r
<number>1</number>\r
</property>\r
</widget>\r
</item>\r
- <item row="2" column="1">\r
- <widget class="QDoubleSpinBox" name="tinc">\r
- <property name="maximum">\r
- <double>10.000000000000000</double>\r
- </property>\r
- <property name="singleStep">\r
- <double>0.250000000000000</double>\r
- </property>\r
- <property name="value">\r
- <double>1.000000000000000</double>\r
- </property>\r
- </widget>\r
+ <item row="7" column="1">\r
+ <widget class="QComboBox" name="experiment"/>\r
</item>\r
- <item row="3" column="1">\r
- <widget class="QDoubleSpinBox" name="finc">\r
- <property name="minimum">\r
- <double>0.000000000000000</double>\r
- </property>\r
- <property name="maximum">\r
- <double>10.000000000000000</double>\r
- </property>\r
- <property name="singleStep">\r
- <double>0.250000000000000</double>\r
- </property>\r
- <property name="value">\r
- <double>1.000000000000000</double>\r
- </property>\r
- </widget>\r
+ <item row="8" column="1">\r
+ <widget class="QComboBox" name="gesture"/>\r
</item>\r
</layout>\r
</item>\r
- <item row="1" column="0">\r
- <widget class="QPushButton" name="buttonstart">\r
- <property name="locale">\r
- <locale language="English" country="Canada"/>\r
- </property>\r
- <property name="text">\r
- <string>Start experiment</string>\r
- </property>\r
- </widget>\r
- </item>\r
</layout>\r
</item>\r
+ <item row="1" column="0">\r
+ <widget class="QPushButton" name="buttonstart">\r
+ <property name="locale">\r
+ <locale language="English" country="Canada"/>\r
+ </property>\r
+ <property name="text">\r
+ <string>Start experiment</string>\r
+ </property>\r
+ </widget>\r
+ </item>\r
</layout>\r
</widget>\r
</widget>\r