Module gpsparser
[hide private]
[frames] | no frames]

Source Code for Module gpsparser

  1  #!/usr/bin/env python 
  2   
  3  __version__ = '$Revision: 4799 $'.split()[1] 
  4  __date__ = '$Date: 2008-11-20 11:09:02 -0400 (Mon, 25 Sep 2006) $'.split()[1] 
  5  __author__ = 'Val Schmidt' 
  6  __doc__ = ''' 
  7  GPSparser, a Python GPS NMEA String parsing module.  
  8   
  9  This module provides GPS NMEA string parsing through the GPSString 
 10  class. A GPSstring is an object whose I{msg} is any ASCII text string 
 11  containing a NMEA GPS string somewhere within it. NMEA strings 
 12  characteristicly start with the leading "$" and end with the *hh where 
 13  hh is the two character checksum. All of the following examples are fine: 
 14   
 15      - C{$GPGGA,154809.00,4305.52462642,N,07051.89568468,W,1,3,4.1,48.971,M,-32.985,M,,*54} 
 16      - C{RTK1_GPS         DATA    2008-11-21T15:48:10.510017      $GPGGA,154809.00,4305.52462642,N,07051.89568468,W,1,3,4.1,48.971,M,-32.985,M,,*5} 
 17      - C{09/12/2003,04:01:46.666,$GPGGA,040145.871,7117.3458,N,15700.3788,W,1,06,1.4,014.6,M,-000.4,M,,*50} 
 18      - C{posnav  2008:226:18:31:34.1365  $INGGA,183133.898,7120.91996,N,15651.72629,W,1,11,0.8,-1.06,M,,,,*19} 
 19   
 20  GPSparser provides methods for extracting the NMEA string from the 
 21  larger set of ASCII characters, verifying its checksum and parsing the 
 22  string's fields. The fields may then be accessed as attributes of GPSString 
 23  (i.e. C{GPSString.latitude}). The complete set of attributes available 
 24  after a string has been parsed can be found in the attribute list 
 25  C{GPSString.fieldnames}. Some fields are not returned, such as those that 
 26  provide units which, by standard or convention, never seem to change.  
 27   
 28  GPSparser is designed for follow-on processing and plotting of values, 
 29  and therefore every effort is made to convert all fields to 
 30  meaningful, numeric values (objects of the Decimal Class). For example, Latitude and 
 31  Longitude are converted to decimal degrees to 10 digits past the 
 32  decimal point. Similarly, GPS fix type in RMC strings are indicated by an "A" 
 33  when the GPS has a fix and a "V" when it does not. These values are 
 34  reported by GPSParser as 1 and 0, respectively.  
 35   
 36  Time in GPS strings are converted to datetime objects (see the 
 37  datetime module). Many NMEA strings contain time-of-day, but not date 
 38  (GGA for example). GPSparser will, by default, convert these strings 
 39  to datetime.time objects. However if the GPSString.date attribute is 
 40  set with a datetime.date object prior to calling the parse() method, a 
 41  full datetime.datetime object will be returned combining the supplied 
 42  date and time parsed from the string. This is a handy way to produce 
 43  full date-time stamps for each string. 
 44   
 45  A common thing to overlook, however, when parsing a file of strings 
 46  from a parent script, in which the times rotate over a UTC day, one 
 47  must be sure to also rotate the value used to set the GPSString.date 
 48  attribute. 
 49   
 50  When the module is called as a script, it will parse a file for a 
 51  single string type, which must be specified on the command-line (see 
 52  C{gpsparser.py -h}). The fields are written in tab-delimited format to 
 53  standard-out. Date-time stamps are written as tab-delimited vectors 
 54  (C{YYYY MM DD HH MM SS}). This format makes reading parsed data files 
 55  into Octave or MATLAB trivial ( C{load('datafile')} ), with the notable 
 56  exception of GSV strings which have variable numbers of fields 
 57  depending on the number of satellites tracked. 
 58   
 59  @author: U{'''+__author__+'''<http://aliceandval.com/valswork>} 
 60  @organization: Center for Coastal and Ocean Mapping, University of New Hampshire  
 61  @version: ''' + __version__ +''' 
 62  @copyright: 2008 
 63  @status: under development 
 64  @license: GPL 
 65   
 66  @todo: Unit tests. There are none so far and there needs to be.  
 67   
 68  ''' 
 69   
 70  import os 
 71  import sys 
 72  import datetime 
 73  import re 
 74  import string 
 75  import decimal as dec 
 76  import pdb 
 77  from operator import xor 
 78  import exceptions  
 79   
80 -class GPSString(object):
81 ''' 82 A GPSString is any string that contains a complete NMEA string someplace 83 within it. The string must start with the leading $ and end with the *hh 84 where hh is the checksum. 85 ''' 86 GPS_IDs = { \ 87 'GGA' : 1, \ 88 'ZDA' : 2, \ 89 'RMC' : 3, \ 90 'GST' : 4, \ 91 'GSV' : 5, \ 92 'VTG' : 6, \ 93 'HDT' : 7 ,\ 94 'PASHR' : 8} 95 96
97 - def __init__(self,msg):
98 ''' 99 Initializes the class with any string containing a single NMEA data string. 100 101 @param msg: The ASCII string containing the NMEA data string. 102 103 ''' 104 self.msg = msg 105 'The message containing the gps string.' 106 self.debug = False 107 'A flag for writing debugging information'
108
109 - def identify(self):
110 ''' 111 This method identifies the string within gps_string, returning an ID index 112 which is required to parse the string. 113 114 Currently the following message types are supported: 115 GGA, ZDA, RMC, GST, GSV, VTG, HDT, PASHR 116 ''' 117 'A dictionary of supported strings and their numeric indentifiers' 118 119 for key in self.GPS_IDs.keys(): 120 if re.search( key, self.msg): 121 self.id = self.GPS_IDs[key] 122 return self.GPS_IDs[key] 123 124 raise NotImplementedError, ("This string is not recognized: " + self.msg)
125
126 - def parse(self):
127 ''' 128 This method pareses a GPSString, defining a set of attributes for the class 129 with the parsing results. How each string is parsed is dependent on the 130 type of string. In any case, the fields returned are defined in 131 "self.fieldnames". 132 133 Before parsing the string's checksum if verified. The GPSString.FailedChecksum 134 exception is raised if the checksum fails. [NOTE: The checksum verification 135 may cause problems for some gps systems which do not calculate the checksum 136 on the proper portion of the string. The NMEA standard specifies calculation 137 on the portions of the string __between__ the leading "$" and "*", but 138 not to include either. ] 139 140 A few general rules are in order. Time stamps are converted to datetime 141 objects. Several GPS strings contain only time fields with no year, month, 142 or day. If gps_string.date is defined with a datetime.date object (i.e. 143 mygpsstring.date = datetime.date(2008,12,1) ) prior to calling the 144 parse() method, the final datetime object will combine the pre-set 145 date with the gps parsed time value. If gps_string.date is not defined 146 the returned datetime object returned from the parse() method will 147 reflect the gps time as a datetime.time() object. 148 149 Latitude and Longitude are converted to decimal degrees with negative 150 values for the Southern and Western hemispheres. They are reported to 8 151 decimal places which equates to just over 1 mm precision. 152 153 Some fields are not parsed because they do not typically change. The 154 units fields of meters for geoid separation in the GGA string is a classic 155 example. 156 157 ''' 158 159 ' Verify Checksum' 160 if not self.checksum(True): 161 raise self.FailedChecksum, ("Checksum: " + self.checksum()) 162 163 164 if self.id == 1: 165 'GGA' 166 exp = '(?P<match>\$..GGA.*)\*(?P<chksum>..)' 167 m = re.search(exp,self.msg) 168 if m: 169 gps_extract = m.group('match') 170 fields = gps_extract.split(',') 171 172 'Handle GGA Fields' 173 self.handlegpstime(fields[1]) 174 self.handle_lat( fields[2], fields[3] ) 175 self.handle_lon ( fields[4], fields[5] ) 176 self.quality = dec.Decimal( fields[6] ) 177 self.svs = dec.Decimal( fields[7] ) 178 self.hdop = dec.Decimal( fields[8]) 179 self.antennaheightMSL = dec.Decimal(fields[9]) 180 try: 181 self.geoid = dec.Decimal(fields[11]) 182 except dec.InvalidOperation: 183 if self.debug: 184 print "The field GEOID Height may not be present." 185 print fields[11] 186 self.geoid = dec.Decimal('NaN') 187 try: 188 self.dgpsage = dec.Decimal(fields[13]) 189 except dec.InvalidOperation: 190 if self.debug: 191 print "The field DGPS Age may not be present." 192 print fields[13] 193 self.dgpspage = dec.Decimal('NaN') 194 try: 195 self.stationid = dec.Decimal(fields[14] ) 196 except dec.InvalidOperation: 197 if self.debug: 198 print "The field DGPS Station ID may not be present." 199 print fields[14] 200 self.stationid = dec.Decimal('NaN') 201 elif self.id == 2: 202 'ZDA' 203 exp = '(?P<match>\$..ZDA.*)\*(?P<chksum>..)' 204 m = re.search(exp,self.msg) 205 if m: 206 gps_extract = m.group('match') 207 fields = gps_extract.split(',') 208 'Handle ZDA Fields' 209 self.datetime = datetime.date(int( fields[4]), \ 210 int(fields[3]), \ 211 int(fields[2])) 212 self.handlegpstime(fields[1]) 213 try: 214 self.tzoffsethours = dec.Decimal( fields[5] ) 215 except dec.InvalidOperation: 216 if self.debug: 217 print "Thef ield Local TZ Offset Hours may not be present." 218 print fields[5] 219 self.tzoffsethours = dec.Decimal('NaN') 220 221 try: 222 self.tzoffsetminutes = dec.Decimal( fields[6] ) 223 except dec.InvalidOperation: 224 if self.debug: 225 print "The field Local TZ Offset Minutes may not be present." 226 print fields[6] 227 self.tzoffsetminutes = dec.Decimal('NaN') 228 229 230 elif self.id == 3: 231 'RMC' 232 exp = '(?P<match>\$..RMC.*)\*(?P<chksum>..)' 233 m = re.search(exp,self.msg) 234 if m: 235 gps_extract = m.group('match') 236 fields = gps_extract.split(',') 237 'Handle RMC Fields' 238 'Getting the date first ensure handlegpstime will return a full' 239 'datetime object' 240 self.datetime.date(int(fields[9][4:6]+2000), 241 int(fields[9][2:4]), 242 int(fields[10][0:2])) 243 self.handlgpstime(fields[1]) 244 if fields[2] == 'A': 245 self.fixstatus = 1 246 else: 247 self.fixstatus = 0 248 self.handlegpslat(fields[3], fields[4]) 249 self.handlegpslon(fields[5], fields[6]) 250 self.knots = fields[7] 251 self.cog = fields[8] 252 253 elif self.id == 4: 254 'GST' 255 exp = '(?P<match>\$..GST.*)\*(?P<chksum>..)' 256 m = re.search(exp,self.msg) 257 if m: 258 gps_extract = m.group('match') 259 fields = gps_extract.split(',') 260 'Handle GST Fields' 261 self.handlegpstime(fields[1]) 262 self.residualrms = dec.Decimal(fields[2]) 263 self.semimajor = dec.Decimal(fields[3]) 264 self.semiminor = dec.Decimal(fields[4]) 265 self.orientation = dec.Decimal(fields[5]) 266 self.lat1sigma = dec.Decimal(fields[6]) 267 self.lon1sigma = dec.Decimal(fields[7]) 268 self.height1sigma = dec.Decimal(fields[8]) 269 270 elif self.id == 5: 271 'GSV' 272 exp = '(?P<match>\$..GSV.*)\*(?P<chksum>..)' 273 m = re.search(exp,self.msg) 274 if m: 275 gps_extract = m.group('match') 276 fields = gps_extract.split(',') 277 'Handle GSV Fields' 278 self.messages = dec.Decimal( fields[1] ) 279 self.messagenum = dec.Decimal ( fields[2] ) 280 self.visibleSVs = dec.Decimal ( fields[3] ) 281 self.PRN = [] 282 self.elevation = [] 283 self.azimuth = [] 284 self.snr = [] 285 if self.debug: 286 print fields 287 for idx in range(4,fields.__len__() - 1, 4): 288 self.PRN.append(dec.Decimal(fields[idx])) 289 self.elevation.append(dec.Decimal(fields[idx + 1])) 290 try: 291 self.azimuth.append(dec.Decimal(fields[idx + 2])) 292 except dec.InvalidOperation: 293 self.azimuth.append(dec.Decimal('NaN')) 294 print "The field Satellite Azimuth may be missing." 295 print fields[idx + 3] 296 try: 297 self.snr.append(dec.Decimal(fields[idx + 3])) 298 except dec.InvalidOperation: 299 # The spec says snr should be null when "not tracking" 300 self.snr.append(dec.Decimal('NaN')) 301 302 elif self.id == 6: 303 'VTG' 304 exp = '(?P<match>\$..VTG.*)\*(?P<chksum>..)' 305 m = re.search(exp,self.msg) 306 if m: 307 gps_extract = m.group('match') 308 fields = gps_extract.split(',') 309 'Handle VTG Fields' 310 self.cog = dec.Decimal(fields[1]) 311 self.knots = dec.Decimal(fields[5]) 312 self.kmph = dec.Decimal(fields[7]) 313 314 315 elif self.id == 7: 316 'HDT' 317 exp = '(?P<match>\$..HDT.*)\*(?P<chksum>..)' 318 m = re.search(exp,self.msg) 319 if m: 320 gps_extract = m.group('match') 321 fields = gps_extract.split(',') 322 'Handle HDT Fields' 323 self.heading = fields[1] 324 elif self.id == 8: 325 'PASHR' 326 exp = '(?P<match>\$PASHR.*)\*(?P<chksum>..)' 327 m = re.search(exp,self.msg) 328 if m: 329 gps_extract = m.group('match') 330 fields = gps_extract.split(',') 331 'Handle PASHR Fields' 332 self.handlegpstime(fields[1]) 333 self.heading = dec.Decimal(fields[2]) 334 self.roll = dec.Decimal(fields[4]) 335 self.pitch = dec.Decimal(fields[5]) 336 self.heave = dec.Decimal(fields[6]) 337 self.rollaccuracy = dec.Decimal(fields[7]) 338 self.pitchaccuracy = dec.Decimal(fields[8]) 339 self.headingaccuracy = dec.Decimal(fields[9]) 340 self.headingalgorithm = dec.Decimal(fields[10]) 341 self.imustatus = dec.Decimal(fields[11]) 342 343 # Create a dictionary of the fields parsed. 344 keys = self.__dict__.keys() 345 keys.remove('debug') 346 keys.remove('msg') 347 keys.remove('id') 348 self.fields = {} 349 for item in keys: 350 self.fields[item] = self.__getattribute__(item)
351
352 - def handlegpstime(self, timestr):
353 ''' 354 An internal method to convert gps time strings to datetime.time objects 355 (default) or datetime.datetime objects when GPSString.date is pre-defined 356 with a datetime.date object. 357 358 @param timestr: A NMEA time string of the form HHMMSS.SSS . 359 360 Since many strings do not contain the date, 361 defining the 'date' attribute of GPSString allows one to manually set 362 the date. 363 ''' 364 tmptime = timestr 365 hour = dec.Decimal(tmptime[0:2]) 366 try: 367 minute = dec.Decimal(tmptime[2:4]) 368 except dec.InvalidOperation: 369 print timestr 370 print tmptime[2:4] 371 print self.msg 372 sys.exit() 373 374 seconds = int(dec.Decimal(tmptime[4:tmptime.__len__()])) 375 microseconds = int( (dec.Decimal(tmptime[4:tmptime.__len__()]) - \ 376 dec.Decimal(seconds) ) * 1000000 ) 377 378 timeval = datetime.time(hour, minute, seconds, microseconds) 379 380 try: 381 self.datetime = datetime.datetime.combine(self.date, timeval) 382 except: 383 self.datetime = timeval
384
385 - def handle_lat(self,lattmp, lathem):
386 ''' 387 Converts latitude strings of arbitrary precision to decimal degrees to 388 10 decimal places of precision (about .000001 meters). 389 390 @param lattmp: The NMEA latitude string. (DDMM.MMMM) 391 @param lathem: The NMEA latitude hemisphere ('N'/'S') 392 ''' 393 self.latitude = '%.10f' % (dec.Decimal(lattmp[0:2]) + 394 dec.Decimal(lattmp[3:lattmp.__len__()]) / 60) 395 self.latitude = dec.Decimal(self.latitude) 396 if lathem == 'S': 397 self.latitude = - self.latitude
398
399 - def handle_lon(self,lontmp, lonhem):
400 ''' 401 Converts longitude strings of arbitrary precision to decimal degrees to 402 10 decimal places of precision (about .000001 meters at the equator). 403 404 @param lontmp: The NMEA longitude string. (DDDMM.MMMM) 405 @param lonhem: The NMEA longitude hemisphere ('E'/'W') 406 ''' 407 self.longitude = '%.10f' % (dec.Decimal(lontmp[0:3]) + 408 dec.Decimal(lontmp[4:lontmp.__len__()]) / 60) 409 self.longitude = dec.Decimal(self.longitude) 410 if lonhem == 'W': 411 self.longitude = - self.longitude
412
413 - def stripisotime(self):
414 ''' 415 Strips and ISO 8601 time stamp from the GPSString and returns a datetime 416 object. 417 418 For many scientific applications GPS strings are logged with the 419 logging computer's date-time stamp. When these time stamps are in 420 ISO 8601 format, this method will extract and parse them, returning a 421 datetime object. 422 ''' 423 iso_exp = re.compile('(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d)T(?P<hour>\d\\d):(?P<minute>\d\d):(?P<seconds>\d\d\.\d+)') 424 m = re.search(iso_exp,self.msg) 425 if m: 426 year = int( m.group('year')) 427 month = int( m.group('month')) 428 day = int( m.group('day')) 429 hour = int(m.group('hour')) 430 minute = int(m.group('minute')) 431 seconds = int(float(m.group('seconds'))) 432 microseconds = int( ( float(m.group('seconds')) - seconds ) * 1000000) 433 dts = datetime.datetime(year, month, day, hour, minute, seconds, microseconds) 434 return dts
435
436 - def datetimevec(self,dts):
437 ''' 438 Converts a datetime stamp in the form of a datetime object to a 439 tab-delimited vector of numeric values. 440 441 @param dts: A datetime object. 442 443 For many scientific applications one often desires to import data into MATLAB 444 or Octave. A simple way to do that is to produce a tab-delimited date time 445 stamp of the form: 446 447 YYYY MH DD HR MN SS 448 449 whiich can be converted to MATLABs internal representation of time with 450 datevec(). This method returns such as time stamp from a datetime object. 451 ''' 452 return "\t".join(map(str,( dts.year, dts.month, dts.day, dts.hour, dts.minute, float(dts.second) + float(dts.microsecond) / 1000000 )))
453
454 - def checksum(self, verify = False):
455 ''' 456 A function to calculate and return a checksum of a NMEA string. 457 458 @param verify: When specified as True, checksum returns True/False 459 rather than the acutal checksum value. 460 461 ''' 462 exp = '(?P<match>\$.*)\*(?P<chksum>..)' 463 m = re.search(exp,self.msg) 464 if m: 465 data = m.group('match') 466 tmp = map(ord, data[1:]) 467 checksum = hex(reduce(xor, tmp)) 468 if verify: 469 return checksum[2:4].upper() == m.group('chksum') 470 else: 471 return checksum[2:4].upper()
472 473 # I'm keeping this because I once had code that simply returned the checksum for 474 # a string (that may not be a well-formatted NMEA string) which is required above. 475 # I'm not convinced this level of generality is necessary or appropriate here just yet. 476 477 # if data.find('*') != -1 : 478 # hasstar = True 479 # tmp = map(ord, data[1:data.index('*')]) 480 # else: 481 # tmp = map(ord, data) 482 # 483 # checksum = reduce(xor, tmp) 484 # checksum = hex(checksum) 485 # return checksum[2:4] 486
487 - class FailedChecksum(Warning):
488 ''' 489 A class creating the exception FailedChecksum, which is derived from the 490 standard exception.Warning. This exception is raised when a GPS string's 491 checksum is incorrect indicating corruption to the data. 492 ''' 493 pass
494 495 ###################################################################################### 496 ######################## Module Code Ends Here. ###################################### 497 ###################################################################################### 498 499 if __name__ == '__main__': 500 501 gpstmp = GPSString('') 502 supportedstrings = gpstmp.GPS_IDs.keys() 503 supportedstrings.sort() 504 supportedstrings = ' '.join(supportedstrings) 505 506 from optparse import OptionParser 507 optionparser = OptionParser(usage="%prog [options]", 508 version="%prog "+__version__+' ('+__date__+')') 509 optionparser.add_option('-f','--filename', dest='filename',action='store', 510 help='specify the filename') 511 optionparser.add_option('-s','--stringtype',dest='stringtype',action='store', 512 type='string', help='specify which string to parse by specifying the three-letter identifier (Currently supported strings: '+ supportedstrings + ' )') 513 514 (options,args) = optionparser.parse_args() 515 516 filename = options.filename 517 stringtype = options.stringtype 518 519 for line in file(filename,'r'): 520 gps = GPSString(line) 521 try: 522 id = gps.identify() 523 except NotImplementedError: 524 sys.stderr.write('Unrecognized NMEA string.\n') 525 continue 526 527 'Only handle string specified' 528 if id != gps.GPS_IDs[stringtype]: 529 continue 530 531 'This will die silently if there is not pc timestamp.' 532 PCtime = gps.stripisotime() 533 534 if id == 1: 535 try: 536 gps.date = PCtime.date() 537 except: 538 gps.date = datetime.datetime.utcnow().date() 539 540 try: 541 gps.parse() 542 except gps.FailedChecksum: 543 sys.stderr.write( "Failed Checksum: " + gps.checksum() + 544 " :: " + gps.msg + '\n') 545 continue 546 547 fieldstoprint = [gps.datetimevec(PCtime), 548 gps.datetimevec(gps.datetime), 549 gps.latitude, 550 gps.longitude, 551 gps.quality, 552 gps.svs, 553 gps.hdop, 554 gps.antennaheightMSL, 555 gps.geoid] 556 print "\t".join(map(str,fieldstoprint)).expandtabs() 557 558 if id == 2: 559 try: 560 gps.date = PCtime.date() 561 except: 562 gps.date = datetime.datetime.utcnow().date() 563 564 try: 565 gps.parse() 566 except gps.FailedChecksum: 567 sys.stderr.write( "Failed Checksum: " + gps.checksum() + 568 " :: " + gps.msg + '\n') 569 570 continue 571 572 fieldstoprint = [gps.datetimevec(PCtime), 573 gps.datetimevec(gps.datetime)] 574 575 print "\t".join(map(str,fieldstoprint)).expandtabs() 576 577 if id == 3: 578 try: 579 gps.date = PCtime.date() 580 except: 581 gps.date = datetime.datetime.utcnow().date() 582 try: 583 gps.parse() 584 except self.FailedChecksum: 585 sys.stderr.write( "Failed Checksum: " + gps.checksum() + 586 " :: " + gps.msg + '\n') 587 continue 588 589 fieldstoprint = [gps.datetimevec(PCtime), 590 gps.datetimevec(gps.datetime), 591 gps.fixstatus, 592 gps.latitude, 593 gps.longitude, 594 gps.knots, 595 gps.cog] 596 print "\t".join(map(str,fieldstoprint)).expandtabs() 597 598 if id == 4: 599 try: 600 gps.date = PCtime.date() 601 except: 602 gps.date = datetime.datetime.utcnow().date() 603 604 try: 605 gps.parse() 606 except self.FailedChecksum: 607 sys.stderr.write( "Failed Checksum: " + gps.checksum() + 608 " :: " + gps.msg + '\n') 609 continue 610 611 fieldstoprint = [gps.datetimevec(PCtime), 612 gps.datetimevec(gps.datetime), 613 gps.residualrms, 614 gps.semimajor, 615 gps.semiminor, 616 gps.orientation, 617 gps.lat1sigma, 618 gps.lon1sigma, 619 gps.height1sigma] 620 print "\t".join(map(str,fieldstoprint)).expandtabs() 621 622 if id == 5: 623 try: 624 gps.date = PCtime.date() 625 except: 626 gps.date = datetime.datetime.utcnow().date() 627 628 try: 629 gps.parse() 630 except self.FailedChecksum: 631 sys.stderr.write( "Failed Checksum: " + gps.checksum() + 632 " :: " + gps.msg + '\n') 633 continue 634 635 fieldstoprint = ([gps.datetimevec(PCtime)] + 636 map(str,gps.PRN) + 637 map(str,gps.elevation) + 638 map(str,gps.azimuth) + 639 map(str,gps.snr) ) 640 print "\t".join(map(str,fieldstoprint)).expandtabs() 641 642 643 if id == 6: 644 try: 645 gps.parse() 646 except self.FailedChecksum: 647 sys.stderr.write( "Failed Checksum:" + gps.checksum() + 648 "::" + gps.msg + '\n') 649 continue 650 651 652 653 if id == 8: 654 try: 655 gps.parse() 656 except self.FailedChecksum: 657 sys.stderr.write( "Failed Checksum:" + gps.checksum() + 658 "::" + gps.msg + '\n') 659 continue 660