00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038 #ifndef PSYCHO_EYETRACE_C_DEFINED
00039 #define PSYCHO_EYETRACE_C_DEFINED
00040
00041 #include "Psycho/EyeTrace.H"
00042 #include "Util/StringConversions.H"
00043 #include "Util/StringUtil.H"
00044 #include "Util/log.H"
00045 #include "rutz/compat_cmath.h"
00046
00047 #include <fstream>
00048
00049
00050 EyeTrace::EyeTrace(const std::string& filename, const PixRGB<byte>& color) :
00051 itsFilename(filename), itsColor(color), itsPeriod(SimTime::ZERO()),
00052 itsTrash(0), itsPPD(), itsNumEvents(0), itsData()
00053 {
00054
00055 const char *fn = filename.c_str();
00056 std::ifstream fil(fn);
00057 if (fil.is_open() == false) PLFATAL("Cannot open '%s'", fn);
00058
00059 std::string line; int linenum = -1;
00060 const std::string delim(" \t");
00061 bool gotperiod = false, gottrash = false, gotppd = false, gotcols = false;
00062 int ncols = 0;
00063
00064 uint samp_count = 0, trashed = 0;
00065 rutz::shared_ptr<ParamMap> lastSacData(new ParamMap);
00066
00067
00068 while (getline(fil, line))
00069 {
00070
00071 ++linenum;
00072
00073
00074 std::string::size_type pos = line.find_first_not_of(delim, 0);
00075
00076 if (pos == line.npos) continue;
00077
00078 if (line[pos] == '#') continue;
00079
00080
00081 if (line.find('=') != line.npos)
00082 {
00083
00084 std::vector<std::string> tok;
00085 split(line, "= \t", std::back_inserter(tok));
00086
00087 if (tok.size() != 2 && tok[0].compare("cols") != 0)
00088 {
00089 LFATAL("Error parsing '%s', line %d", fn, linenum);
00090 }
00091
00092 if (tok[0].compare("period") == 0)
00093 { itsPeriod = SimTime::fromString(tok[1]); gotperiod = true; }
00094 else if (tok[0].compare("trash") == 0)
00095 { itsTrash = fromStr<int>(tok[1]); gottrash = true; }
00096 else if (tok[0].compare("ppd") == 0)
00097 {
00098
00099 std::vector<std::string> ltok;
00100 split(tok[1], ",", std::back_inserter(ltok));
00101 if (ltok.size() > 1)
00102 itsPPD = PixPerDeg(fromStr<float>(ltok[0]),
00103 fromStr<float>(ltok[1]));
00104 else
00105 itsPPD = PixPerDeg(fromStr<float>(ltok[0]), 0.0F);
00106 gotppd = true;
00107 }
00108 else if (tok[0].compare("cols") == 0)
00109 {
00110 tok.erase(tok.begin());
00111 itsFields = tok;
00112 gotcols = true;
00113 }
00114 else LFATAL("Unknown parameter '%s', file '%s' line %d",
00115 tok[0].c_str(), fn, linenum);
00116
00117
00118
00119 if (gotperiod && gottrash && gotppd) break;
00120
00121
00122 continue;
00123 }
00124
00125
00126 LFATAL("I need to have period, trash, and ppd information before "
00127 "data starts, file '%s' line %d", fn, linenum);
00128 }
00129
00130 LINFO("%s: period = %.3fms, ppdx = %.1f ppdy = %.1f pix/deg, trash = %"ZU" samples.",
00131 fn, itsPeriod.msecs(), itsPPD.ppdx(), itsPPD.ppdy(), itsTrash);
00132
00133
00134
00135
00136
00137 while (getline(fil, line))
00138 {
00139
00140 ++linenum;
00141
00142
00143 std::string::size_type pos = line.find_first_not_of(delim, 0);
00144
00145 if (pos == line.npos) continue;
00146
00147 if (line[pos] == '#') continue;
00148
00149
00150 if (line.find("cols =") != line.npos)
00151 {
00152
00153 std::vector<std::string> tok;
00154 split(line, "= \t", std::back_inserter(tok));
00155
00156 tok.erase(tok.begin());
00157 itsFields = tok;
00158 gotcols = true;
00159 continue;
00160 }
00161
00162
00163 if(!gotcols)
00164 {
00165
00166 std::vector<std::string> values;
00167 split(line, " ", std::back_inserter(values));
00168 ncols = values.size();
00169
00170 std::string flds = "";
00171
00172
00173
00174 switch(ncols)
00175 {
00176 case 2: {flds = "x y"; break;}
00177 case 3: {flds = "x y status"; break;}
00178 case 4: {flds = "x y pd status"; break;}
00179 case 7: {flds = "x y status *targetx *targety *ampl *interval"; break;}
00180 case 8: {flds = "x y pd status *targetx *targety *ampl *interval"; break;}
00181 default:
00182 LFATAL("Error parsing data in '%s',line %d: %d of columns gives unknown data",
00183 fn, linenum, ncols);
00184 }
00185
00186 split(flds, " ", std::back_inserter(itsFields));
00187 gotcols = true;
00188 }
00189
00190
00191 if (trashed < itsTrash) { ++trashed; continue; }
00192
00193
00194 else {pushData(line);}
00195
00196
00197 samp_count++;
00198 }
00199
00200 LINFO("%s: cols = %s",
00201 fn, join(itsFields.begin(),itsFields.end()," ").c_str() );
00202
00203 LINFO("%s: %zu samples, %u events.", fn, itsData.size(),
00204 itsNumEvents);
00205 }
00206
00207
00208 EyeTrace::~EyeTrace()
00209 { }
00210
00211
00212 bool EyeTrace::hasData(const size_t index, const SimTime& t) const
00213 { return ( index < itsData.size() && itsPeriod * int(index) < t ); }
00214
00215
00216 bool EyeTrace::hasData(const size_t index) const
00217 { return ( index < itsData.size() ); }
00218
00219
00220 rutz::shared_ptr<EyeData> EyeTrace::data(const size_t index) const
00221 {
00222 if (index >= itsData.size()) LFATAL("Index past end of trace");
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233
00234 SaccadeState ss; bool bs;
00235 switch(itsData[index].status)
00236 {
00237 case 0: ss = SACSTATE_FIX; bs = false; break;
00238 case 1: ss = SACSTATE_SAC; bs = false; break;
00239 case 2: ss = SACSTATE_FIX; bs = true; break;
00240 case 3: ss = SACSTATE_SAC; bs = true; break;
00241 case 4: ss = SACSTATE_SMO; bs = false; break;
00242 case 5: ss = SACSTATE_UNK; bs = false; break;
00243 case 6: ss = SACSTATE_COM; bs = false; break;
00244
00245 default:
00246 LFATAL("Bogus status code '%d', file '%s' index %"ZU" (after trashing)",
00247 itsData[index].status, itsFilename.c_str(), index);
00248 ss = SACSTATE_UNK; bs = false;
00249 break;
00250 }
00251
00252 rutz::shared_ptr<EyeData> ret;
00253
00254
00255
00256 ret.reset(new EyeData(itsData[index].x, itsData[index].y,
00257 itsData[index].diam, ss, bs,
00258 itsData[index].extraData));
00259
00260 return ret;
00261 }
00262
00263
00264 const SaccadeState EyeTrace::getStatus(const int sta) const
00265 {
00266 SaccadeState ss;
00267 switch(sta)
00268 {
00269 case 0: ss = SACSTATE_FIX; break;
00270 case 1: ss = SACSTATE_SAC; break;
00271 case 2: ss = SACSTATE_FIX; break;
00272 case 3: ss = SACSTATE_SAC; break;
00273 case 4: ss = SACSTATE_SMO; break;
00274 case 5: ss = SACSTATE_UNK; break;
00275 case 6: ss = SACSTATE_COM; break;
00276
00277 default:
00278 LFATAL("Bogus status code '%d', file '%s'",
00279 sta, itsFilename.c_str());
00280 ss = SACSTATE_UNK;
00281 break;
00282 }
00283 return ss;
00284
00285 }
00286
00287 bool EyeTrace::pushData(const std::string line)
00288 {
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301 std::vector<std::string> strVals;
00302 split(line, " ", std::back_inserter(strVals));
00303
00304 if(itsFields.size() != strVals.size())
00305 {}
00306
00307
00308 rutz::shared_ptr<ParamMap> dataBuffer(new ParamMap);
00309
00310
00311 const double SENTINEL = 0.0;
00312 uint i = 0;
00313 std::vector<double> values(strVals.size(),SENTINEL);
00314 for (i = 0; i < strVals.size(); i++)
00315 if(strVals[i].compare("NaN") != 0)
00316 values[i] = fromStr<double>(strVals[i]);
00317
00318
00319
00320
00321 bool hasEventData = false;
00322 for (i = 0; i < itsFields.size(); i++)
00323
00324 if (isEventField(itsFields[i]) && values[i] != SENTINEL)
00325 {
00326 hasEventData = true;
00327 break;
00328 }
00329
00330
00331
00332
00333
00334
00335
00336
00337 std::vector<std::string> key_fields;
00338 const std::string fieldnames("x y pd status");
00339 split(fieldnames," ", std::back_inserter(key_fields));
00340 std::vector<double> defaults(key_fields.size(),0);
00341
00342
00343 for (i = 0; i < key_fields.size(); i++)
00344 dataBuffer->putDoubleParam(key_fields[i], defaults[i]);
00345
00346
00347 for (i = 0; i < itsFields.size(); i++)
00348 {
00349
00350 if (isEventField(itsFields[i]) && !hasEventData) continue;
00351
00352 if(find(key_fields.begin(),key_fields.end(),itsFields[i])
00353 !=key_fields.end() )
00354 dataBuffer->replaceDoubleParam(itsFields[i], values[i]);
00355 else
00356 dataBuffer->putDoubleParam(itsFields[i],values[i]);
00357 }
00358
00359
00360
00361 RawEyeData ed = {float(dataBuffer->getDoubleParam("x")),
00362 float(dataBuffer->getDoubleParam("y")),
00363 float(dataBuffer->getDoubleParam("pd")),
00364 int(dataBuffer->getDoubleParam("status")),
00365 dataBuffer};
00366
00367 for (i = 0; i < key_fields.size(); i++)
00368 ed.extraData->erase(key_fields[i]);
00369
00370
00371 if(hasEventData)
00372 {
00373
00374 itsEvents.push_back(ed.extraData);
00375 itsNumEvents++;
00376 }
00377
00378 itsData.push_back(ed);
00379 return true;
00380 }
00381
00382
00383 bool EyeTrace::isEventField(const std::string field) const
00384 { return field[0] == '*'; }
00385
00386
00387 size_t EyeTrace::size() const
00388 { return itsData.size(); }
00389
00390
00391 size_t EyeTrace::numEvents() const
00392 { return itsNumEvents; }
00393
00394
00395 size_t EyeTrace::numSaccades() const
00396 { LERROR("This function is no longer accurate, use numEvents instead.");
00397 return itsNumEvents; }
00398
00399
00400
00401
00402 SimTime EyeTrace::period() const
00403 { return itsPeriod; }
00404
00405
00406 std::string EyeTrace::filename() const
00407 { return itsFilename; }
00408
00409
00410 PixRGB<byte> EyeTrace::color() const
00411 { return itsColor; }
00412
00413
00414 std::string EyeTrace::basename() const
00415 {
00416 size_t idx = itsFilename.rfind('.');
00417 if (idx != itsFilename.npos) return itsFilename.substr(0, idx);
00418
00419 return itsFilename;
00420 }
00421
00422
00423 PixPerDeg EyeTrace::ppd() const
00424 { return itsPPD; }
00425
00426
00427
00428
00429
00430
00431
00432
00433 #endif // PSYCHO_EYETRACE_C_DEFINED