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

ArGPS.cpp

00001 /*
00002 MobileRobots Advanced Robotics Interface for Applications (ARIA)
00003 Copyright (C) 2004, 2005 ActivMedia Robotics LLC
00004 Copyright (C) 2006, 2007 MobileRobots Inc.
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 MobileRobots for information about a commercial version of ARIA at 
00022 robots@mobilerobots.com or 
00023 MobileRobots Inc, 19 Columbia Drive, Amherst, NH 03031; 800-639-9481
00024 */
00025 
00026 
00027 #include "ArExport.h"
00028 #include "ariaOSDef.h"
00029 #include "ArGPS.h"
00030 
00031 #include "Aria.h"
00032 #include <iostream>
00033 
00034 
00035 
00036 /* 
00037  * How to add support for new GPS data:
00038  * ------------------------------------
00039  *
00040  *  1. Add a variable to store the data to the ArGPS declaration in ArGPS.h,
00041  *     including a flag that indicates whether that data is valid (the "haveXXX"
00042  *     booleans). Initialize that flag in the ArGPS constructor definition below.
00043  *
00044  *  2. Create a handler method and functor for the NMEA message that provides
00045  *     the data. Initialize the functor in the constructor below.
00046  *
00047  *  3. Add the functor to the handler map myHandlers in the constructor below. 
00048  *
00049  *  4. Implement the handler method to examine the field (don't forget that 
00050  *     NMEA does not require that all fields be given).
00051  *
00052  *  5. Add a line to the initialization commands above which are required to 
00053  *     start some GPS systems to start sending data.
00054  *
00055  * Some possible new NMEA message types to add are GPGST (RAIM residuals and
00056  * error information), GPMSS (beacon signal info), PTNLDG (Trimble proprietary
00057  * DGPS status information), and GPZDA (time of day information).
00058  *
00059  *
00060  *
00061  * How to add support for new GPS types:
00062  * -------------------------------------
00063  *
00064  * If your GPS device uses NMEA and does not require any special initialization
00065  * commands, then it will probably work if you use the right BAUD rate.
00066  *
00067  * If your GPS device is special, then you may need to define a subclass
00068  * of ArGPS. You can override connect(), setDeviceType(), and/or read() to do 
00069  * special things. See ArNovatelGPS as an example. Then add support to
00070  * it to ArGPSConnector: add a new member of the GPSType enum, a check for
00071  * it in parseArgs(), mention it in logArgs(), and create your ArGPS subclass
00072  * in createGP().
00073  *
00074  * You can find out the NMEA messages ArGPS wants by accessing "myHandlers",
00075  * of type HandlersMap (a std::map).
00076  * 
00077  */
00078 
00079 
00080 
00081 
00090 AREXPORT ArGPS::ArGPS() :
00091 
00092   // Data
00093   myHavePosition(false),
00094   myHaveSpeed(false),
00095   myGPSPositionTimestamp(0), 
00096   myFixType(NoFix), 
00097   myNumSatellitesTracked(0),
00098   myHaveAltitude(false),
00099   myHaveDGPSStation(false),
00100   myHavePositionError(false),
00101   myHaveVerticalPositionError(false),
00102   myHaveCompassHeading(false),
00103   myHaveHDOP(false), 
00104   myHaveVDOP(false), 
00105   myHavePDOP(false),
00106 
00107   // objects
00108   myDevice(NULL),
00109 
00110   // handler functors
00111   myGPRMCHandler(this, &ArGPS::handleGPRMC),
00112   myGPGGAHandler(this, &ArGPS::handleGPGGA),
00113   myPGRMEHandler(this, &ArGPS::handlePGRME),
00114   myPGRMZHandler(this, &ArGPS::handlePGRMZ),
00115   myHCHDGHandler(this, &ArGPS::handleHCHDG),
00116   myGPGSAHandler(this, &ArGPS::handleGPGSA),
00117 
00118   // parse state
00119   MaxNumFields(50),
00120   MaxFieldSize(128),
00121   ignoreChecksum(false),
00122   checksumBufOffset(0),
00123   inChecksum(false),
00124   inMessage(false),
00125   currentChecksum(0),
00126   gotCR(false)
00127 {
00128   myHandlers["GPRMC"] = &myGPRMCHandler;
00129   myHandlers["GPGGA"] = &myGPGGAHandler;
00130   myHandlers["PGRME"] = &myPGRMEHandler;
00131   myHandlers["PGRMZ"] = &myPGRMZHandler;
00132   myHandlers["HCHDG"] = &myHCHDGHandler;
00133   myHandlers["GPGSA"] = &myGPGSAHandler;
00134 }
00135 
00136 AREXPORT ArGPS::~ArGPS()
00137 {
00138   memset(checksumBuf, 0, 3);
00139 }
00140 
00141 
00142 
00143 
00144 
00145 
00146 AREXPORT bool ArGPS::connect()
00147 {
00148   if (!myDevice)
00149   {
00150     ArLog::log(ArLog::Terse, "GPS Error: Cannot connect, device connection invalid.");
00151     return false;
00152   }
00153 
00154   if (myDevice->getStatus() != ArDeviceConnection::STATUS_OPEN) 
00155   {
00156     ArLog::log(ArLog::Terse, "GPS Error: Cannot connect, device connection not open.");
00157     return false;
00158   }
00159 
00160 
00161   return true;
00162 }
00163 
00164 
00165 
00166 AREXPORT bool ArGPS::blockingConnect(unsigned long connectTimeout)
00167 {
00168   ArTime start;
00169   start.setToNow();
00170   if (!connect()) return false;
00171   while ((unsigned long)start.mSecSince() <= connectTimeout)
00172   {
00173     if (read() & ReadUpdated)  // read until data is sucessfully parsed 
00174       return true;
00175     ArUtil::sleep(100);
00176     // Maybe try connect() again every few seconds?
00177   }
00178   return false;
00179 }
00180 
00181 
00182 
00183 void ArGPS::nextField()
00184 {
00185   currentMessage.push_back(currentField);
00186   currentField = "";
00187   if (currentMessage.size() > MaxNumFields)
00188     endMessage();
00189 }
00190 
00191 void ArGPS::endMessage()
00192 {
00193   inMessage = false;
00194   inChecksum = false;
00195   currentField = "";
00196   gotCR = false;
00197 }
00198 
00199 void ArGPS::beginChecksum()
00200 {
00201   checksumBufOffset = 0;
00202   inChecksum = true;
00203 }
00204 
00205 void ArGPS::beginMessage()
00206 {
00207   currentMessage.clear();
00208   inChecksum = false;
00209   inMessage = true;
00210   currentField = "";
00211   gotCR = false;
00212   currentChecksum = 0;
00213 }
00214 
00215 
00216 AREXPORT int ArGPS::read()
00217 {
00218   if (!myDevice) return ReadError;
00219   char buf[256]; // move into LexState struct?
00220 
00221   int result = 0;
00222 
00223   // keep reading data, returning when there's no data left or there's an error.
00224   while(true)
00225   {
00226     int n = myDevice->read(buf, sizeof(buf));
00227 
00228     if (n < 0) 
00229     {
00230       return result|ReadError;
00231     }
00232 
00233     if (n == 0) 
00234     {
00235       return result|ReadFinished;
00236     }
00237 
00238     //std::cerr << "\t[ArGPS: parsing chunk \"" << buf << "\"]\n";
00239 
00240     for (int i = 0; i < n; i++)
00241     {
00242       // Check for message start
00243       if (buf[i] == '$')
00244       {
00245         beginMessage();
00246         continue;
00247       }
00248 
00249       // Otherwise, we must be in a sentece to do anything
00250       if (!inMessage)
00251         continue;
00252 
00253       // Reached the CRLF at the end?
00254       if (buf[i] == '\r') 
00255       {
00256         gotCR = true;
00257         continue;
00258       }
00259       if (buf[i] == '\n') 
00260       {
00261         if (gotCR) 
00262         {
00263           endMessage();
00264           HandlerMap::iterator h = myHandlers.find(currentMessage[0]);
00265           if (h != myHandlers.end()) 
00266           {
00267             //printf("\t[Calling handler for %s...]\n", currentMessage[0].c_str());
00268             h->second->invoke(&currentMessage);
00269             result |= ReadUpdated;
00270           }
00271         }
00272 
00273         // a syntax error, abort the message and start looking for the next one.
00274         endMessage();
00275         continue;
00276       }
00277 
00278       // Are we in the final checksum field?
00279       if (inChecksum)
00280       {
00281         checksumBuf[checksumBufOffset++] = buf[i];
00282         if (checksumBufOffset > 1)   // two bytes of checksum
00283         {
00284           int checksumRec = (int) strtol(checksumBuf, NULL, 16);
00285           //printf("\t[End checksum. Message provided checksum \"%s\", => %x. Calculated checksum is %x (%d).]\n", checksumBuf, checksumRec, currentChecksum, currentChecksum);
00286           if (checksumRec != currentChecksum) {
00287             ArLog::log(ArLog::Normal, "GPS: Warning: Skipping message with incorrect checksum.");
00288             // abort the message and start looking for the next one.
00289             endMessage();
00290           }
00291         }
00292         continue;
00293       }
00294 
00295 
00296       // Got to the checksum?
00297       if (buf[i] == '*')
00298       {
00299         nextField();
00300         if (!ignoreChecksum)
00301           beginChecksum();
00302         continue;
00303       }
00304 
00305       // Every other byte in a message (between $ and *) XORs to form the
00306       // checksum:
00307       currentChecksum ^= buf[i];
00308 
00309       // Time to start a new field?
00310       if (buf[i] == ',')
00311       {
00312         nextField();
00313         continue;
00314       }
00315 
00316 
00317       // Else, we must be in the middle of a field
00318       // TODO we could use strchr to look ahead in the buf 
00319       // for the end of the field (',' or '*') or end of the buf, and copy more
00320       // than one byte at a time.
00321       currentField += buf[i];
00322       if (currentField.size() > MaxFieldSize)
00323       {
00324         endMessage();
00325         continue;
00326       }
00327     }
00328   }
00329   return result;
00330 }
00331 
00332 
00333 
00334 
00335 // Key navigation data (position, etc.)
00336 void ArGPS::handleGPRMC(MessageVector* message)
00337 {
00338   // Good data?:
00339   if (message->size() < 3 || (*message)[2] != "A") return;
00340 
00341   if (!readFloatFromStringVec(message, 3, &myLatitude, &gpsDegminToDegrees)) return;
00342 
00343   if (message->size() < 5) return;
00344   if ((*message)[4] == "S") myLatitude *= -1;
00345 
00346   if (!readFloatFromStringVec(message, 5, &myLongitude, &gpsDegminToDegrees)) return;
00347 
00348   if (message->size() < 7) return;
00349   if ((*message)[6] == "W") myLongitude *= -1;
00350 
00351   // Only set these if we got both position components:
00352   myHavePosition = true;
00353   myGPSPositionTimestamp = atoi((*message)[1].c_str());
00354 
00355   myHaveSpeed = readFloatFromStringVec(message, 7, &mySpeed, &knotsToMPS);
00356 
00357 }
00358 
00359 // Fix type, number of satellites tracked, DOP and also maybe altitude
00360 void ArGPS::handleGPGGA(MessageVector* message)
00361 {
00362   if (message->size() < 7) return;
00363   switch(atoi((*message)[6].c_str()))
00364   {
00365     case 0:
00366       myFixType = BadFix;
00367       break;
00368     case 1: 
00369       myFixType = GPSFix;
00370       break;
00371     case 2:
00372       myFixType = DGPSFix;
00373       break;
00374     case 3:
00375       myFixType = PPSFix;
00376       break;
00377     case 4:
00378       myFixType = RTKinFix;
00379       break;
00380     case 5:
00381       myFixType = FloatRTKinFix;
00382       break;
00383     case 6:
00384       myFixType = DeadReckFix;
00385       break;
00386     case 7: 
00387       myFixType = ManualFix;
00388       break;
00389     case 8:
00390       myFixType = SimulatedFix;
00391       break;
00392     default:
00393       myFixType = UnknownFixType;
00394   }
00395   
00396   readUShortFromStringVec(message, 7, &myNumSatellitesTracked);
00397   myHaveHDOP = readFloatFromStringVec(message, 8, &myHDOP); // note redundant with GPGSA
00398   myHaveAltitude = readFloatFromStringVec(message, 9, &myAltitude); // might be redundante with PGRMZ
00399   myHaveDGPSStation = readUShortFromStringVec(message, 14, &myDGPSStationID);
00400 }
00401 
00402 
00403 // Error estimation in ground distance units (actually a proprietary message)
00404 void ArGPS::handlePGRME(MessageVector* message)
00405 {
00406   // TODO: check units; keep as DOP or convert to something else?
00407   // Use GPGSA instead of PGRME?
00408   myHavePositionError = readFloatFromStringVec(message, 1, &myPositionError);
00409   myHaveVerticalPositionError = readFloatFromStringVec(message, 3, &myVerticalPositionError);
00410 }
00411 
00412 // Altitude (actually a Garmin proprietary message)
00413 void ArGPS::handlePGRMZ(MessageVector* message)
00414 {
00415   // This is redundant with GPGGA and often a different value (plus the
00416   // conversion...) Favor this over that one, or separate into two values?
00417   // Yes, if this is specifically from an altimiter and the value in GGA is
00418   // from the satellite positions.
00419   if (myHaveAltitude && message->size() >= 3 && strcasecmp((*message)[2].c_str(), "f") == 0)
00420     myAltitude = feetToMeters(myAltitude);
00421 }
00422 
00423 // Compass heading (actually a proprietary message)
00424 void ArGPS::handleHCHDG(MessageVector* message)
00425 {
00426   myHaveCompassHeading = readFloatFromStringVec(message, 1, &myCompassHeading);
00427 }
00428 
00429 // GPS DOP and satellite IDs
00430 void ArGPS::handleGPGSA(MessageVector* message)
00431 {
00432   // This message alse has satellite IDs, not sure if that information is
00433   // useful though.
00434   
00435   myHavePDOP = readFloatFromStringVec(message, 15, &myPDOP);
00436   myHaveHDOP = readFloatFromStringVec(message, 16, &myHDOP);
00437   myHaveVDOP = readFloatFromStringVec(message, 17, &myVDOP);
00438 }
00439 
00440 AREXPORT const char* ArGPS::getFixTypeName() const 
00441 {
00442   switch (getFixType())
00443   {
00444     case NoFix: return "None";
00445     case BadFix: return "Bad";
00446     case GPSFix: return "GPS";
00447     case DGPSFix: return "DGPS";
00448     case PPSFix: return "PPS";
00449     case RTKinFix: return "RT Kinematic";
00450     case FloatRTKinFix: return "Float. RT Kinematic";
00451     case DeadReckFix: return "Dead Reckoning";
00452     case ManualFix: return "Manual";
00453     case SimulatedFix: return "Simulated";
00454     default: return "Unknown";
00455   }
00456 }
00457 
00458 AREXPORT void ArGPS::logData() const
00459 {
00460   ArLog::log(ArLog::Normal, "GPS Fix=%s Num. Satellites=%d", getFixTypeName(), getNumSatellitesTracked());
00461   
00462   if (havePosition())
00463   {
00464     ArLog::log(ArLog::Normal, "GPS Latitude=%0.4fdeg Longitude=%0.4fdeg Timestamp=%d", getLatitude(), getLongitude(), getGPSPositionTimestamp());
00465     // for  fun... ArLog::log(ArLog::Normal, "GPS Maps: <http://www.topozone.com/map.asp?lat=%f&lon=%f&datum=nad83&u=5>  <http://maps.google.com/maps?q=%f,+%f>", getLatitude(), getLongitude(), getLatitude(), getLongitude());
00466   }
00467   
00468   if (haveSpeed())
00469     ArLog::log(ArLog::Normal, "GPS Speed=%0.4fm/s (%0.4fmi/h)", getSpeed(), mpsToMph(getSpeed()));
00470 
00471   if (haveAltitude())
00472     ArLog::log(ArLog::Normal, "GPS Altitude=%0.4fm (%0.4fft)", getAltitude(), metersToFeet(getAltitude()));
00473 
00474   if (haveCompassHeading())
00475     ArLog::log(ArLog::Normal, "GPS Compass Heading=%0.4fdeg", getCompassHeading());
00476 
00477   if (havePositionError())
00478     ArLog::log(ArLog::Normal, "GPS Position Error Estimate=%0.4fm", getPositionError());
00479 
00480   if (haveVerticalPositionError())
00481     ArLog::log(ArLog::Normal, "GPS Vertical Position Error Estimate=%0.4fm", getVerticalPositionError());
00482 
00483   if (havePDOP())
00484     ArLog::log(ArLog::Normal, "GPS PDOP=%0.4f", getPDOP());
00485   if (haveHDOP())
00486     ArLog::log(ArLog::Normal, "GPS HDOP=%0.4f", getHDOP());
00487   if (haveVDOP())
00488     ArLog::log(ArLog::Normal, "GPS VDOP=%0.4f", getVDOP());
00489 
00490   if (haveDGPSStation())
00491     ArLog::log(ArLog::Normal, "GPS DGPS Station ID=%d", getDGPSStationID());
00492 
00493 }
00494 
00495 AREXPORT void ArGPS::printData() const
00496 {
00497   printf("GPS: ");
00498   if (!havePosition())
00499   {
00500     printf("Pos:-");
00501   }
00502   else
00503   {
00504     printf("Pos:% 2.6f,% 2.6f", getLatitude(), getLongitude());
00505   }
00506 
00507   if (!haveSpeed())
00508     printf("   Spd:-");
00509   else
00510     printf("   Spd:%4.4fm/s (%3.4fmi/h)", getSpeed(), mpsToMph(getSpeed()));
00511 
00512   if (!haveAltitude())
00513     printf("   Alt:-");
00514   else
00515     printf("   Alt:%4.2fm (%4.2fft)", getAltitude(), metersToFeet(getAltitude()));
00516 
00517   if (!haveCompassHeading())
00518     printf("   Head:-");
00519   else
00520     printf("   Head:%3.1fdeg", getCompassHeading());
00521 
00522   printf("   NSats:%2d", getNumSatellitesTracked());
00523 
00524   if (!havePositionError())
00525     printf("   ErrEst:-");
00526   else
00527     printf("   ErrEst:%2.4fm", getPositionError());
00528 
00529   if (haveHDOP())
00530     printf("   HDOP:%1.2f", getHDOP());
00531   else
00532     printf("   HDP:-");
00533 
00534   printf("   Fix:%-10s", getFixTypeName());
00535   printf("   (%d)", getGPSPositionTimestamp());
00536 }
00537 
00538 
00539 double ArGPS::gpsDegminToDegrees(double degmin) 
00540 {
00541   double degrees;
00542   double minutes = modf(degmin / 100.0, &degrees) * 100.0;
00543   return degrees + (minutes / 60.0);
00544 }
00545 
00546 
00547 double ArGPS::knotsToMPS(double knots) 
00548 {
00549   return(knots * 0.514444444);
00550 }
00551 
00552 
00553 bool ArGPS::readFloatFromString(std::string& str, double* target, double (*convf)(double))
00554 {
00555   if (str.length() == 0) return false;
00556   if (convf)
00557     *target = (*convf)(atof(str.c_str()));
00558   else
00559     *target = atof(str.c_str());
00560   return true;
00561 }
00562 
00563 bool ArGPS::readUShortFromString(std::string& str, unsigned short* target, unsigned short (*convf)(unsigned short))
00564 {
00565   if (str.length() == 0) return false;
00566   if (convf)
00567     *target = (*convf)(atof(str.c_str()));
00568   else
00569     *target = (unsigned short) atoi(str.c_str());
00570   return true;
00571 }
00572 
00573 
00574 bool ArGPS::readFloatFromStringVec(std::vector<std::string>* vec, size_t i, double* target, double (*convf)(double))
00575 {
00576   if (vec->size() < (i+1)) return false;
00577   return readFloatFromString((*vec)[i], target, convf);
00578 }
00579 
00580 bool ArGPS::readUShortFromStringVec(std::vector<std::string>* vec, size_t i, unsigned short* target, unsigned short (*convf)(unsigned short))
00581 {
00582   if (vec->size() < (i+1)) return false;
00583   return readUShortFromString((*vec)[i], target, convf);
00584 }
00585 
00586 

Generated on Tue Feb 20 10:51:40 2007 for Aria by  doxygen 1.4.0