trialevent.cc

Go to the documentation of this file.
00001 
00003 
00004 //
00005 // Copyright (c) 1999-2004 California Institute of Technology
00006 // Copyright (c) 2004-2007 University of Southern California
00007 // Rob Peters <rjpeters at usc dot edu>
00008 //
00009 // created: Fri Jun 25 12:44:55 1999
00010 // commit: $Id: trialevent.cc 10065 2007-04-12 05:54:56Z rjpeters $
00011 // $HeadURL: file:///lab/rjpeters/svnrepo/code/trunk/groovx/src/visx/trialevent.cc $
00012 //
00013 // --------------------------------------------------------------------
00014 //
00015 // This file is part of GroovX.
00016 //   [http://ilab.usc.edu/rjpeters/groovx/]
00017 //
00018 // GroovX is free software; you can redistribute it and/or modify it
00019 // under the terms of the GNU General Public License as published by
00020 // the Free Software Foundation; either version 2 of the License, or
00021 // (at your option) any later version.
00022 //
00023 // GroovX is distributed in the hope that it will be useful, but
00024 // WITHOUT ANY WARRANTY; without even the implied warranty of
00025 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00026 // General Public License for more details.
00027 //
00028 // You should have received a copy of the GNU General Public License
00029 // along with GroovX; if not, write to the Free Software Foundation,
00030 // Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
00031 //
00033 
00034 #ifndef GROOVX_VISX_TRIALEVENT_CC_UTC20050626084017_DEFINED
00035 #define GROOVX_VISX_TRIALEVENT_CC_UTC20050626084017_DEFINED
00036 
00037 #include "visx/trialevent.h"
00038 
00039 #include "io/ioproxy.h"
00040 #include "io/outputfile.h"
00041 #include "io/reader.h"
00042 #include "io/readutils.h"
00043 #include "io/writer.h"
00044 #include "io/writeutils.h"
00045 
00046 #include "nub/log.h"
00047 #include "nub/ref.h"
00048 
00049 #include "tcl/eventloop.h"
00050 #include "tcl/timerscheduler.h"
00051 
00052 #include "rutz/algo.h"
00053 #include "rutz/error.h"
00054 #include "rutz/iter.h"
00055 #include "rutz/sfmt.h"
00056 #include "rutz/shared_ptr.h"
00057 
00058 #include "visx/trial.h"
00059 
00060 #include <fstream>
00061 
00062 #include "rutz/trace.h"
00063 #include "rutz/debug.h"
00064 GVX_DBG_REGISTER
00065 
00066 using rutz::fstring;
00067 
00069 //
00070 // TrialEvent method definitions
00071 //
00073 
00074 TrialEvent::TrialEvent(unsigned int msec) :
00075   itsTimer(msec, false),
00076   itsRequestedDelay(msec),
00077   itsTrial(0),
00078   itsEstimatedOffset(0.0),
00079   itsTotalOffset(0.0),
00080   itsTotalError(0.0),
00081   itsInvokeCount(0)
00082 {
00083 GVX_TRACE("TrialEvent::TrialEvent");
00084 
00085   itsTimer.sig_timeout.connect(this, &TrialEvent::invokeTemplate);
00086 }
00087 
00088 TrialEvent::~TrialEvent() throw()
00089 {
00090 GVX_TRACE("TrialEvent::~TrialEvent");
00091 
00092   dbg_eval(3, itsTotalOffset);
00093   dbg_eval(3, itsTotalError);
00094   dbg_eval(3, itsInvokeCount);
00095   double averageError =
00096     itsInvokeCount ? itsTotalError/itsInvokeCount : 0.0;
00097   dbg_eval_nl(3, averageError);
00098 }
00099 
00100 void TrialEvent::read_from(io::reader& reader)
00101 {
00102 GVX_TRACE("TrialEvent::read_from");
00103 
00104   cancel(); // cancel since the event is changing state
00105 
00106   reader.read_value("requestedDelay", itsRequestedDelay);
00107 }
00108 
00109 void TrialEvent::write_to(io::writer& writer) const
00110 {
00111 GVX_TRACE("TrialEvent::write_to");
00112 
00113   writer.write_value("requestedDelay", itsRequestedDelay);
00114 }
00115 
00116 unsigned int TrialEvent::schedule(rutz::shared_ptr<nub::scheduler> s,
00117                                   Trial& trial,
00118                                   unsigned int minimum_msec)
00119 {
00120 GVX_TRACE("TrialEvent::schedule");
00121 
00122   // Remember the participants
00123   itsTrial = &trial;
00124 
00125   // Figure out the delay we want to request. If our requested delay
00126   // is zero, then just leave it as is, so that we get an immediate
00127   // callback from the scheduler. Otherwise, adjust the request
00128   // according to how much error we expect based on past observations.
00129 
00130   unsigned int actual_request = 0;
00131 
00132   if (itsRequestedDelay > 0)
00133     actual_request =
00134       rutz::max(int(itsRequestedDelay) + int(itsEstimatedOffset),
00135                 int(minimum_msec));
00136 
00137   itsTimer.set_delay_msec(actual_request);
00138   itsTimer.schedule(s);
00139 
00140   return actual_request;
00141 }
00142 
00143 void TrialEvent::cancel()
00144 {
00145 GVX_TRACE("TrialEvent::cancel");
00146   itsTimer.cancel();
00147 }
00148 
00149 void TrialEvent::invokeTemplate()
00150 {
00151 GVX_TRACE("TrialEvent::invokeTemplate");
00152 
00153   const double msec = itsTimer.elapsed_msec();
00154   const double error = itsTimer.delay_msec() - msec;
00155 
00156   nub::logging::add_obj_scope(*this);
00157 
00158   nub::log( rutz::sfmt("req %u - %f",
00159                        itsRequestedDelay, -itsEstimatedOffset) );
00160 
00161   itsTotalOffset += error;
00162   itsTotalError += (itsRequestedDelay - msec);
00163 
00164   ++itsInvokeCount;
00165 
00166   // Positive error: we expect the event to occur sooner than requested
00167   // Negative error: we expect the event to occur later than requested
00168   // (round towards negative infinity)
00169   const double moving_average_ratio =
00170     1.0 / rutz::min(10, itsInvokeCount);
00171 
00172   itsEstimatedOffset =
00173     (1.0 - moving_average_ratio) * itsEstimatedOffset +
00174     moving_average_ratio  * error;
00175 
00176   // Do the actual event callback.
00177   if ( itsTrial != 0 )
00178     {
00179       invoke(*itsTrial);
00180     }
00181 
00182   nub::logging::remove_obj_scope(*this);
00183 }
00184 
00185 //---------------------------------------------------------------------
00186 //
00187 // NullTrialEvent
00188 //
00189 //---------------------------------------------------------------------
00190 
00191 NullTrialEvent::NullTrialEvent(unsigned int msec) : TrialEvent(msec) {}
00192 
00193 NullTrialEvent::~NullTrialEvent() throw() {}
00194 
00195 void NullTrialEvent::invoke(Trial&) {}
00196 
00197 //---------------------------------------------------------------------
00198 //
00199 // TrialMemFuncEvent
00200 //
00201 //---------------------------------------------------------------------
00202 
00203 fstring TrialMemFuncEvent::obj_typename() const
00204 {
00205   return itsTypename;
00206 }
00207 
00208 TrialMemFuncEvent* TrialMemFuncEvent::make(CallbackType callback,
00209                                            const fstring& type,
00210                                            unsigned int msec)
00211 {
00212   return new TrialMemFuncEvent(callback, type, msec);
00213 }
00214 
00215 TrialMemFuncEvent::TrialMemFuncEvent(CallbackType callback,
00216                                      const fstring& type,
00217                                      unsigned int msec) :
00218   TrialEvent(msec),
00219   itsCallback(callback),
00220   itsTypename(type)
00221 {}
00222 
00223 TrialMemFuncEvent::~TrialMemFuncEvent() throw() {}
00224 
00225 void TrialMemFuncEvent::invoke(Trial& trial)
00226 {
00227 GVX_TRACE("TrialMemFuncEvent::invoke");
00228   (trial.*itsCallback)();
00229 }
00230 
00231 #define MAKE_EVENT(EventName)                           \
00232 TrialEvent* make##EventName##Event()                    \
00233 {                                                       \
00234   return TrialMemFuncEvent::make(&Trial::tr##EventName, \
00235                                  #EventName "Event");   \
00236 }
00237 
00238 MAKE_EVENT(AbortTrial);
00239 MAKE_EVENT(Draw);
00240 MAKE_EVENT(Render);
00241 MAKE_EVENT(EndTrial);
00242 MAKE_EVENT(NextNode);
00243 MAKE_EVENT(AllowResponses);
00244 MAKE_EVENT(DenyResponses);
00245 MAKE_EVENT(Undraw);
00246 MAKE_EVENT(RenderBack);
00247 MAKE_EVENT(RenderFront);
00248 MAKE_EVENT(SwapBuffers);
00249 MAKE_EVENT(ClearBuffer);
00250 MAKE_EVENT(FinishDrawing);
00251 
00252 #undef MAKE_EVENT
00253 
00254 //---------------------------------------------------------------------
00255 //
00256 // FileWriteEvent
00257 //
00258 //---------------------------------------------------------------------
00259 
00260 FileWriteEvent::FileWriteEvent(unsigned int msec) :
00261   TrialEvent(msec),
00262   itsFile(output_file::make())
00263 {}
00264 
00265 FileWriteEvent::~FileWriteEvent() throw() {}
00266 
00267 void FileWriteEvent::invoke(Trial& /*trial*/)
00268 {
00269   if (itsFile->has_stream())
00270     {
00271       itsFile->stream().put(static_cast<char>(itsByte));
00272       itsFile->stream().flush();
00273       nub::log( rutz::sfmt("wrote '%d' to '%s'",
00274                            itsByte, itsFile->get_filename().c_str()) );
00275     }
00276 }
00277 
00278 void FileWriteEvent::read_from(io::reader& reader)
00279 {
00280   itsFile = dyn_cast<output_file>(reader.read_object("file"));
00281   reader.read_value("byte", itsByte);
00282 
00283   reader.read_base_class("TrialEvent",
00284                        io::make_proxy<TrialEvent>(this));
00285 }
00286 
00287 void FileWriteEvent::write_to(io::writer& writer) const
00288 {
00289   writer.write_object("file", itsFile);
00290   writer.write_value("byte", itsByte);
00291 
00292   writer.write_base_class("TrialEvent",
00293                         io::make_const_proxy<TrialEvent>(this));
00294 }
00295 
00296 int FileWriteEvent::getByte() const
00297 {
00298   return itsByte;
00299 }
00300 
00301 void FileWriteEvent::setByte(int b)
00302 {
00303   itsByte = b;
00304 }
00305 
00306 nub::ref<output_file> FileWriteEvent::getFile() const
00307 {
00308   return itsFile;
00309 }
00310 
00311 void FileWriteEvent::setFile(nub::ref<output_file> file)
00312 {
00313   itsFile = file;
00314 }
00315 
00316 //---------------------------------------------------------------------
00317 //
00318 // GenericEvent
00319 //
00320 //---------------------------------------------------------------------
00321 
00322 GenericEvent::GenericEvent(unsigned int msec) :
00323   TrialEvent(msec),
00324   itsCallback(new tcl::ProcWrapper(tcl::event_loop::interp()))
00325 {}
00326 
00327 GenericEvent::~GenericEvent() throw() {}
00328 
00329 void GenericEvent::invoke(Trial& /*trial*/)
00330 {
00331   itsCallback->invoke("");
00332 }
00333 
00334 void GenericEvent::read_from(io::reader& reader)
00335 {
00336   reader.read_owned_object("callback", itsCallback);
00337 
00338   reader.read_base_class("TrialEvent",
00339                        io::make_proxy<TrialEvent>(this));
00340 }
00341 
00342 void GenericEvent::write_to(io::writer& writer) const
00343 {
00344   writer.write_owned_object("callback", itsCallback);
00345 
00346   writer.write_base_class("TrialEvent",
00347                         io::make_const_proxy<TrialEvent>(this));
00348 }
00349 
00350 fstring GenericEvent::getCallback() const
00351 {
00352   return itsCallback->fullSpec();
00353 }
00354 
00355 void GenericEvent::setCallback(const fstring& script)
00356 {
00357   itsCallback->define("", script);
00358 }
00359 
00360 //---------------------------------------------------------------------
00361 //
00362 // MultiEvent
00363 //
00364 //---------------------------------------------------------------------
00365 
00366 MultiEvent::MultiEvent(unsigned int msec) :
00367   TrialEvent(msec),
00368   itsEvents()
00369 {}
00370 
00371 MultiEvent::~MultiEvent() throw() {}
00372 
00373 void MultiEvent::invoke(Trial& trial)
00374 {
00375   for (unsigned int i = 0; i < itsEvents.size(); ++i)
00376     {
00377       itsEvents[i]->setDelay(0);
00378       itsEvents[i]->invoke(trial);
00379     }
00380 }
00381 
00382 void MultiEvent::read_from(io::reader& reader)
00383 {
00384   std::vector<nub::ref<TrialEvent> > newEvents;
00385 
00386   io::read_utils::read_object_seq<TrialEvent>
00387     (reader, "events", std::back_inserter(newEvents));
00388 
00389   itsEvents.swap(newEvents);
00390 
00391   reader.read_base_class("TrialEvent", io::make_proxy<TrialEvent>(this));
00392 }
00393 
00394 void MultiEvent::write_to(io::writer& writer) const
00395 {
00396   io::write_utils::write_object_seq(writer, "events",
00397                                     itsEvents.begin(),
00398                                     itsEvents.end());
00399 
00400   writer.write_base_class("TrialEvent",
00401                         io::make_const_proxy<TrialEvent>(this));
00402 }
00403 
00404 rutz::fwd_iter<const nub::ref<TrialEvent> >
00405 MultiEvent::getEvents() const
00406 {
00407 GVX_TRACE("MultiEvent::getEvents");
00408 
00409   return rutz::fwd_iter<const nub::ref<TrialEvent> >
00410     (itsEvents.begin(), itsEvents.end());
00411 }
00412 
00413 unsigned int MultiEvent::addEvent(nub::ref<TrialEvent> event)
00414 {
00415 GVX_TRACE("MultiEvent::addEvent");
00416   itsEvents.push_back(event);
00417   GVX_ASSERT(itsEvents.size() >= 1);
00418   return itsEvents.size() - 1;
00419 }
00420 
00421 void MultiEvent::eraseEventAt(unsigned int index)
00422 {
00423 GVX_TRACE("MultiEvent::eraseEventAt");
00424   if (index >= itsEvents.size())
00425     throw rutz::error("index out of bounds", SRC_POS);
00426 
00427   itsEvents.erase(itsEvents.begin() + index);
00428 }
00429 
00430 void MultiEvent::clearEvents()
00431 {
00432 GVX_TRACE("MultiEvent::clearEvents");
00433   itsEvents.clear();
00434 }
00435 
00436 static const char __attribute__((used)) vcid_groovx_visx_trialevent_cc_utc20050626084017[] = "$Id: trialevent.cc 10065 2007-04-12 05:54:56Z rjpeters $ $HeadURL: file:
00437 #endif // !GROOVX_VISX_TRIALEVENT_CC_UTC20050626084017_DEFINED

The software described here is Copyright (c) 1998-2005, Rob Peters.
This page was generated Wed Dec 3 06:49:42 2008 by Doxygen version 1.5.5.