Main Page | Class Hierarchy | Alphabetical List | Class List | File List | Class Members | Related Pages

ArSickLogger.cpp

00001 /*
00002 ActivMedia Robotics Interface for Applications (ARIA)
00003 Copyright (C) 2004,2005 ActivMedia Robotics, LLC
00004 
00005 
00006      This program is free software; you can redistribute it and/or modify
00007      it under the terms of the GNU General Public License as published by
00008      the Free Software Foundation; either version 2 of the License, or
00009      (at your option) any later version.
00010 
00011      This program is distributed in the hope that it will be useful,
00012      but WITHOUT ANY WARRANTY; without even the implied warranty of
00013      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014      GNU General Public License for more details.
00015 
00016      You should have received a copy of the GNU General Public License
00017      along with this program; if not, write to the Free Software
00018      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019 
00020 If you wish to redistribute ARIA under different terms, contact 
00021 ActivMedia Robotics for information about a commercial version of ARIA at 
00022 robots@activmedia.com or 
00023 ActivMedia Robotics, 19 Columbia Drive, Amherst, NH 03031; 800-639-9481
00024 
00025 */
00026 
00027 #include <stdarg.h>
00028 
00029 #include "ArExport.h"
00030 #include "ariaOSDef.h"
00031 #include "ArSickLogger.h"
00032 #include "ArRobot.h"
00033 #include "ArSick.h"
00034 #include "ArJoyHandler.h"
00035 #include "ariaInternal.h"
00036 
00057 ArSickLogger::ArSickLogger(ArRobot *robot, ArSick *sick, 
00058                                     double distDiff, double degDiff, 
00059                                     const char *fileName, bool addGoals,
00060                                     ArJoyHandler *joyHandler,
00061                                     const char *baseDirectory) :
00062   mySectors(18), 
00063   myTaskCB(this, &ArSickLogger::robotTask),
00064   myGoalKeyCB(this, &ArSickLogger::goalKeyCallback), 
00065   myLoopPacketHandlerCB(this, &ArSickLogger::loopPacketHandler)
00066 {
00067   ArKeyHandler *keyHandler;
00068   
00069   ArSick::Degrees degrees;
00070   ArSick::Increment increment;
00071   double deg, incr;
00072 
00073   myOldReadings = false;
00074   myNewReadings = true;
00075   myWrote = false;
00076   myRobot = robot;
00077   mySick = sick;
00078   if (baseDirectory != NULL && strlen(baseDirectory) > 0)
00079     myBaseDirectory = baseDirectory;
00080   else
00081     myBaseDirectory = "";
00082   std::string realFileName;
00083   if (fileName[0] == '/' || fileName[0] == '\\')
00084   {
00085     realFileName = fileName;
00086   }
00087   else
00088   {
00089     realFileName = myBaseDirectory;
00090     realFileName += fileName;
00091   }
00092   myFileName = realFileName;
00093   
00094   myFile = fopen(realFileName.c_str(), "w+");
00095   degrees = mySick->getDegrees();
00096   increment = mySick->getIncrement();
00097   if (degrees == ArSick::DEGREES180)
00098     deg = 180;
00099   else 
00100     deg = 100;
00101   if (increment == ArSick::INCREMENT_ONE)
00102     incr = 1;
00103   else
00104     incr = .5;
00105   if (myFile != NULL)
00106   {
00107     const ArRobotParams *params;
00108     params = robot->getRobotParams();
00109     fprintf(myFile, "LaserOdometryLog\n");
00110     fprintf(myFile, "#Created by ARIA's ArSickLogger\n");
00111     fprintf(myFile, "version: 2\n");
00112     fprintf(myFile, "sick1pose: %d %d %.2f\n", params->getLaserX(), 
00113             params->getLaserY(), params->getLaserTh());
00114     fprintf(myFile, "sick1conf: %d %d %d\n", 
00115             ArMath::roundInt(0.0 - deg / 2.0),
00116             ArMath::roundInt(deg / 2.0), ArMath::roundInt(deg / incr + 1.0));
00117   }
00118   else
00119     ArLog::log(ArLog::Terse, "ArSickLogger cannot write to file %s", 
00120                myFileName.c_str());
00121 
00122   myDistDiff = distDiff;
00123   myDegDiff = degDiff;
00124   myFirstTaken = false;
00125   myScanNumber = 0;
00126   myLastVel = 0;
00127   myStartTime.setToNow();
00128   myRobot->addUserTask("Sick Logger", 1, &myTaskCB);
00129 
00130   char uCFileName[15];
00131   strncpy(uCFileName, fileName, 14);
00132   uCFileName[14] = '\0';
00133   myRobot->comStr(94, uCFileName);
00134 
00135   myRobot->addPacketHandler(&myLoopPacketHandlerCB);
00136 
00137   myAddGoals = addGoals;
00138   myJoyHandler = joyHandler;
00139   myTakeReadingExplicit = false;
00140   myAddGoalExplicit = false;
00141   myAddGoalKeyboard = false;
00142   myFirstGoalTaken = false;
00143   myNumGoal = 1;
00144   myLastLoops = 0;
00145   // if we're adding goals hook into everything
00146   if (myAddGoals)
00147   {
00148     // do it for the keyboard first
00149     // see if there is already a keyhandler, if not make one for ourselves
00150     if ((keyHandler = Aria::getKeyHandler()) == NULL)
00151     {
00152       keyHandler = new ArKeyHandler;
00153       Aria::setKeyHandler(keyHandler);
00154       myRobot->attachKeyHandler(keyHandler);
00155     }
00156     // now that we have a key handler, add our keys as callbacks, print out big
00157     // warning messages if they fail
00158     if (!keyHandler->addKeyHandler('g', &myGoalKeyCB))
00159       ArLog::log(ArLog::Terse, "The key handler already has a key for g, sick logger goal handling will not work correctly.");
00160     if (!keyHandler->addKeyHandler('G', &myGoalKeyCB))
00161       ArLog::log(ArLog::Terse, "The key handler already has a key for g, sick logger goal handling will not work correctly.");
00162     
00163     // the joystick checks will happen down in the task
00164   }
00165 
00166 
00167 }
00168 
00169 ArSickLogger::~ArSickLogger()
00170 {
00171   myRobot->remUserTask(&myTaskCB);
00172   myRobot->remPacketHandler(&myLoopPacketHandlerCB);
00173   myRobot->comStr(94, "");
00174   if (myFile != NULL)
00175   {
00176     fprintf(myFile, "# End of log\n");
00177     fclose(myFile);
00178   }
00179 }
00180   
00181 bool ArSickLogger::loopPacketHandler(ArRobotPacket *packet)
00182 {
00183   unsigned char loops;
00184   if (packet->getID() != 0x96)
00185     return false;
00186   loops = packet->bufToUByte();
00187   unsigned char bit;
00188   int num;
00189   if (loops != myLastLoops)
00190   {
00191     for (bit = 1, num = 1; num <= 8; bit *= 2, num++)
00192     {
00193       if ((loops & bit) && !(myLastLoops & bit))
00194       {
00195         addTagToLog("loop: start %d", num);
00196         ArLog::log(ArLog::Normal, "Starting loop %d", num);
00197       }
00198       else if (!(loops & bit) && (myLastLoops & bit))
00199       {
00200         addTagToLog("loop: end %d", num);
00201         ArLog::log(ArLog::Normal, "Ending loop %d", num);
00202       }
00203     }
00204   }
00205   myLastLoops = loops;
00206   return true;
00207 }
00208 
00217 void ArSickLogger::addTagToLogPlain(const char *str)
00218 {
00219   myTags.push_back(str);
00220 }
00221 
00230 void ArSickLogger::addTagToLog(const char *str, ...)
00231 {
00232   char buf[2048];
00233   va_list ptr;
00234   va_start(ptr, str);
00235   vsprintf(buf, str, ptr);
00236   addTagToLogPlain(buf);
00237   va_end(ptr);
00238 }
00239 
00240 
00249 void ArSickLogger::addInfoToLogPlain(const char *str)
00250 {
00251   myInfos.push_back(str);
00252 }
00253 
00261 void ArSickLogger::addInfoToLog(const char *str, ...)
00262 {
00263   char buf[2048];
00264   va_list ptr;
00265   va_start(ptr, str);
00266   vsprintf(buf, str, ptr);
00267   addInfoToLogPlain(buf);
00268   va_end(ptr);
00269 }
00270 
00271 void ArSickLogger::goalKeyCallback(void)
00272 {
00273   myAddGoalKeyboard = true;
00274 }
00275 
00276 void ArSickLogger::internalAddGoal(void)
00277 {
00278   // this check is for if we're not adding goals return... but if
00279   // we're not adding goals and one was requested explicitly then add
00280   // that one
00281   if (!myAddGoals && !myAddGoalExplicit) 
00282     return;
00283 
00284   // make sure we'd even want to add a goal if we could
00285   if (myRobot->isConnected() && 
00286       (!myFirstGoalTaken || 
00287        myAddGoalExplicit ||
00288        myLastGoalTakenPose.findDistanceTo(myRobot->getEncoderPose()) > 300 ||
00289        myLastGoalTakenPose.findAngleTo(myRobot->getEncoderPose()) > 90 ||
00290        myLastGoalTakenTime.secSince() > 10))
00291        
00292   {
00293     // now see if we want to, first by second button on the joystick
00294     // connected to microcontroller... then by if a goal was requested
00295     // (by keypress), then if a goal was requested explicitly then
00296     // finally by the joystick attached to computer
00297     
00298     if ((myRobot->getFlags() & ArUtil::BIT9) || 
00299         myAddGoalKeyboard ||
00300         myAddGoalExplicit || 
00301         (myJoyHandler != NULL && (myJoyHandler->getButton(2) || 
00302                                   myJoyHandler->getButton(3) || 
00303                                   myJoyHandler->getButton(4))))
00304     {
00305       myFirstGoalTaken = true;
00306       myAddGoalExplicit = false;
00307       myLastGoalTakenTime.setToNow();
00308       myLastGoalTakenPose = myRobot->getEncoderPose();
00309       // call addTagToLog not do it directly so we get additional info
00310       // needed
00311       addTagToLog("cairn: GoalWithHeading \"\" ICON_GOALWITHHEADING \"goal%d\"", myNumGoal);
00312       printf("Goal %d taken\n", myNumGoal);
00313       myNumGoal++;
00314     }
00315   }
00316   // reset this here for if they held the key down a little, so it
00317   // gets reset and doesn't hit multiple goals
00318   myAddGoalKeyboard = false;
00319 
00320 }
00321 
00322 void ArSickLogger::internalWriteTags(void)
00323 {
00324   time_t msec;
00325 
00326   // now put the tags into the file
00327   while (myInfos.size() > 0)
00328   {
00329     if (myFile != NULL)
00330     {
00331       myWrote = true;
00332       fprintf(myFile, "%s\n", (*myInfos.begin()).c_str());
00333     }
00334     myInfos.pop_front();
00335   }
00336 
00337 
00338   // now put the tags into the file
00339   while (myTags.size() > 0)
00340   {
00341     if (myFile != NULL)
00342     {
00343       myWrote = true;
00344       msec = myStartTime.mSecSince();
00345       fprintf(myFile, "time: %ld.%ld\n", msec / 1000, msec % 1000);
00346       internalPrintPos(myRobot->getEncoderPose());
00347       fprintf(myFile, "%s\n", (*myTags.begin()).c_str());
00348     }
00349     myTags.pop_front();
00350   }
00351 }
00352 
00353 void ArSickLogger::internalTakeReading(void)
00354 {
00355   const std::list<ArSensorReading *> *readings;
00356   std::list<ArSensorReading *>::const_iterator it;
00357   std::list<ArSensorReading *>::const_reverse_iterator rit;
00358   ArPose poseTaken;
00359   time_t msec;
00360   ArSensorReading *reading;
00361   
00362   // we take readings in any of the following cases if we haven't
00363   // taken one yet or if we've been explicitly told to take one or if
00364   // we've gone further than myDistDiff if we've turned more than
00365   // myDegDiff if we've switched sign on velocity and gone more than
00366   // 50 mm (so it doesn't oscilate and cause us to trigger)
00367 
00368   if (myRobot->isConnected() && (!myFirstTaken || myTakeReadingExplicit || 
00369       myLast.findDistanceTo(myRobot->getEncoderPose()) > myDistDiff ||
00370       fabs(ArMath::subAngle(myLast.getTh(), 
00371                             myRobot->getEncoderPose().getTh())) > myDegDiff ||
00372       ((myLastVel < 0 && myRobot->getVel() > 0 ||
00373         myLastVel > 0 && myRobot->getVel() < 0) && 
00374        myLast.findDistanceTo(myRobot->getEncoderPose()) > 50)))
00375   {
00376     myWrote = true;
00377     mySick->lockDevice();
00378     readings = mySick->getRawReadings();
00379     if (readings == NULL || (it = readings->begin()) == readings->end() ||
00380         myFile == NULL)
00381     {
00382       mySick->unlockDevice();
00383       return;
00384     }
00385     myTakeReadingExplicit = false;
00386     myScanNumber++;
00387     ArLog::log(ArLog::Normal, 
00388                "Taking a reading from the %d laser values", 
00389                readings->size());
00390     myFirstTaken = true;
00391     myLast = myRobot->getEncoderPose();
00392     poseTaken = (*readings->begin())->getEncoderPoseTaken();
00393     myLastVel = myRobot->getVel();
00394     msec = myStartTime.mSecSince();
00395     fprintf(myFile, "#Scan %d\n", myScanNumber);
00396     fprintf(myFile, "time: %ld.%ld\n", msec / 1000, msec % 1000);
00397     /* ScanStudio isn't using these yet so don't print them
00398       fprintf(myFile, "velocities: %.2f %.2f\n", myRobot->getRotVel(),
00399       myRobot->getVel());*/
00400     internalPrintPos(poseTaken);
00401 
00402     if (myOldReadings)
00403     {
00404       fprintf(myFile, "sick1: ");
00405       
00406       if (!mySick->isLaserFlipped())
00407       {
00408         // make sure that the list is in increasing order
00409         for (it = readings->begin(); it != readings->end(); it++)
00410         {
00411           reading = (*it);
00412           fprintf(myFile, "%d ", reading->getRange());
00413         }
00414       }
00415       else
00416       {
00417         for (rit = readings->rbegin(); rit != readings->rend(); rit++)
00418         {
00419           reading = (*rit);
00420           fprintf(myFile, "%d ", reading->getRange());
00421         }
00422       }
00423       fprintf(myFile, "\n");
00424     }
00425     if (myNewReadings)
00426     {
00427       fprintf(myFile, "scan1: ");
00428       
00429       if (!mySick->isLaserFlipped())
00430       {
00431         // make sure that the list is in increasing order
00432         for (it = readings->begin(); it != readings->end(); it++)
00433         {
00434           reading = (*it);
00435           if (!reading->getIgnoreThisReading())
00436             fprintf(myFile, "%.0f %.0f  ", 
00437                     reading->getLocalX() - mySick->getSensorPositionX(), 
00438                     reading->getLocalY() - mySick->getSensorPositionY());
00439           else
00440             fprintf(myFile, "0 0  ");
00441         }
00442       }
00443       else
00444       {
00445         for (rit = readings->rbegin(); rit != readings->rend(); rit++)
00446         {
00447           reading = (*rit);
00448           if (!reading->getIgnoreThisReading())
00449             fprintf(myFile, "%.0f %.0f  ", 
00450                     reading->getLocalX() - mySick->getSensorPositionX(), 
00451                     reading->getLocalY() - mySick->getSensorPositionY());
00452           else
00453             fprintf(myFile, "0 0  ");
00454         }
00455       }
00456       fprintf(myFile, "\n");
00457     }
00458     mySick->unlockDevice();
00459   }
00460 }
00461 
00462 void ArSickLogger::internalPrintPos(ArPose poseTaken)
00463 {
00464   if (myFile == NULL)
00465     return;
00466   ArPose encoderPose = myRobot->getEncoderPose();
00467   ArPose rawEncoderPose = myRobot->getRawEncoderPose();
00468   ArTransform normalToRaw(rawEncoderPose, encoderPose);
00469 
00470   ArPose rawPose;
00471   rawPose = normalToRaw.doInvTransform(poseTaken);
00472   fprintf(myFile, "#rawRobot: %.0f %.0f %.2f %.0f %.0f\n", 
00473           rawPose.getX(), 
00474           rawPose.getY(), 
00475           rawPose.getTh(), 
00476           myRobot->getVel(),
00477           myRobot->getRotVel());
00478   fprintf(myFile, "robot: %.0f %.0f %.2f %.0f %.0f\n", 
00479           poseTaken.getX(), 
00480           poseTaken.getY(), 
00481           poseTaken.getTh(), 
00482           myRobot->getVel(),
00483           myRobot->getRotVel());
00484 }
00485 
00486 void ArSickLogger::robotTask(void)
00487 {
00488 
00489   // call our function to check goals
00490   internalAddGoal();
00491   
00492   // call our function to dump tags
00493   internalWriteTags();
00494   
00495   // call our function to take a reading
00496   internalTakeReading();
00497 
00498   // now make sure the files all out to disk
00499   if (myWrote)
00500   {
00501     fflush(myFile);
00502 #ifndef WIN32
00503     fsync(fileno(myFile));
00504 #endif
00505   }
00506   myWrote = false;
00507 }
00508 
00509 
00510 
00511 

Generated on Wed Oct 19 12:56:37 2005 for Aria by  doxygen 1.4.0