JND exp functionnal
authorThomas Pietrzak <thomas.pietrzak@gmail.com>
Wed, 15 Jun 2011 21:19:28 +0000 (21:19 +0000)
committerThomas Pietrzak <thomas.pietrzak@gmail.com>
Wed, 15 Jun 2011 21:19:28 +0000 (21:19 +0000)
git-svn-id: svn+ssh://thomaspietrzak.com/var/svn/rep@34 47cf9a05-e0a8-4ed5-9e9b-101a649bc004

Tactons Experiment.sln
Tactons Experiment.suo
Tactons JND/Tactons JND.vcxproj
Tactons JND/answer3AFC.cpp
Tactons JND/answer3AFC.h
Tactons JND/main.cpp
Tactons JND/tactonsjnd.cpp
Tactons JND/tactonsjnd.ui

index 4f417b4c4201368d59ad1ba72a727355fbe7e1f2..2f5c05828dd8726eee814bfffeb8d08c22bcd004 100644 (file)
@@ -5,8 +5,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Tactons Experiment", "Tacto
 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
@@ -21,10 +19,6 @@ Global
                {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
index a4343e8558752c71f06b906e5857ce2dbb38cdac..3e7a78110d13201880a35ba7a88bc766aea0dc3c 100644 (file)
Binary files a/Tactons Experiment.suo and b/Tactons Experiment.suo differ
index 83c587dc0d6e0638e2bab9f96edce5c16df4d3c1..8fb9f8cb61799bc028da44c7e5e3ed115332b6b6 100644 (file)
@@ -50,6 +50,7 @@
       <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
index 58ea3b146c6c6001e52efcbb664f78aee045ebff..34cbc13a0fe9e22c149d7c909e3e37c5559172d1 100644 (file)
 #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
index 58ffd2ae833bbf770a8e91626fc7a4522e25323e..a7c711f7fda8b49eb4dbcf76b2f3158d30eb50ed 100644 (file)
@@ -1,6 +1,7 @@
 #ifndef _ANSWER3AFC_\r
 #define _ANSWER3AFC_\r
 \r
+#include <TactonPlayer/Tacton.hpp>\r
 #include <TactonPlayer/TactonPlayer.hpp>\r
 \r
 #include <QtGui/QDialog>\r
@@ -15,30 +16,44 @@ public:
        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
index d0c14924a2ae101eb3e76e45bbf2a5cd79ff502c..d01421685c142cdb5ccd1553b5a5bcfe513ff75d 100644 (file)
@@ -1,8 +1,10 @@
 #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
index 6fedb8c6d5c9d9635751b3352f219e0affc1956d..7274998c48f24346d511e0c86ce3b957221d7db5 100644 (file)
@@ -1,5 +1,7 @@
 #include "tactonsjnd.h"\r
 \r
+#include <QApplication>\r
+\r
 TactonsJND::TactonsJND(QWidget *parent, Qt::WFlags flags)\r
        : QMainWindow(parent, flags)\r
 {\r
@@ -40,3 +42,12 @@ TactonsJND::~TactonsJND()
 {\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
index 35226d9da25b262ff737a33770c30e33fcc78e36..7b979449ff673f2414826ac9e9b7f7d9fcf4ccff 100644 (file)
@@ -6,8 +6,8 @@
    <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
@@ -20,8 +20,8 @@
    <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