1# Copyright (c) 1999-2000 Jo�o Pedro Gon�alves <joaop@sl.pt>. 2#All rights reserved. This program is free software; 3#you can redistribute it and/or modify it under the same terms as Perl itself. 4 5package GPS::NMEA::Handler; 6 7use strict; 8use vars qw($VERSION); 9 10$VERSION = sprintf("%d.%02d", q$Revision: 1.4 $ =~ /(\d+)\.(\d+)/); 11 12use POSIX qw(:termios_h); 13use FileHandle; 14use Carp; 15 16#$|++; 17 18#$GPRMB,A,0.66,L,003,004,4917.24,N,12309.57,W,001.3,052.5,000.5,V*0B 19# 20 21# $GPRMB,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,<10>,<11>,<12>,<13>*hh 22# 1) Data status, A=OK, V=warning 23# 2) Cross-track error (nautical miles, 9.9 max) 24# 3) L=steer left to correct, R=steer right to correct 25# 4) Origin waypoint ID 26# 5) Destination waypoint ID 27# 6) Destination waypoint latitude 28# 7) Destination waypoint latitude hemisphere 29# 8) Destination waypoint longitude 30# 9) Destination waypoint longitude hemisphere 31# 10) Range to destination, nautical miles 32# 11) True bearing to destination 33# 12) Velocity towards destination, knots 34# 13) Arrival alarm, A=Arrived, V=Not arrived 35 36sub GPRMB { 37 # RMB - Data when waypoint destination is active 38 my $self = shift; 39 $self->{NMEADATA} = {} unless ref($self->{NMEADATA}); 40 my $d = $self->{NMEADATA}; 41 42 (undef, 43 $$d{data_valid}, 44 $$d{cross_track_error_naut_miles}, 45 $$d{steer_L_or_R}, 46 $$d{origin_waypoint}, 47 $$d{dest_waypoint}, 48 $$d{dest_waypoint_lat}, 49 $$d{dest_lat_NS}, 50 $$d{dest_waypoint_lon}, 51 $$d{dest_lon_EW}, 52 $$d{range_dest}, 53 $$d{bearing_dest}, 54 $$d{velocity_dest_knots}, 55 $$d{arrival_alarm} 56 ) = split(',',shift); 57 1; 58} 59 60 61#$GPGGA,033850,4748.811,N,12219.564,W,1,04,2.2,202.8,M,-18.3,M,,*7F 62# 63 64# GPS-35: 65# $GPGGA,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,M,<10>,M,<11>,<12>*hh 66# 1) UTC time of position fix, hhmmss format 67# 2) Latitude, ddmm.mmmm format (leading zeroes will be transmitted) 68# 3) Latitude hemisphere, N or S 69# 4) Longitude, dddmm.mmmm format (leading zeros will be transmitted) 70# 5) Longitude hemisphere, E or W 71# 6) GPS quality indication, 0=no fix, 1=non-DGPS fix, 2=DGPS fix 72# 7) Number of sats in use, 00 to 12 73# 8) Horizontal Dilution of Precision 1.0 to 99.9 74# 9) Antenna height above/below mean sea level, -9999.9 to 99999.9 meters 75# 10) Geoidal height, -999.9 to 9999.9 meters 76# 11) DGPS data age, number of seconds since last valid RTCM transmission (null if non-DGPS) 77# 12) DGPS reference station ID, 0000 to 1023 (leading zeros will be sent, null if non-DGPS) 78 79sub GPGGA { 80 # Global Positioning System Fix Data 81 82 my $self = shift; 83 $self->{NMEADATA} = {} unless ref($self->{NMEADATA}); 84 85 my $d = $self->{NMEADATA}; 86 (undef, 87 $$d{time_utc}, 88 $$d{lat_ddmm}, 89 $$d{lat_NS}, 90 $$d{lon_ddmm}, 91 $$d{lon_EW}, 92 $$d{fixq012}, 93 $$d{num_sat_tracked}, 94 $$d{hdop}, 95 $$d{alt_meters}, 96 $$d{alt_meters_units}, 97 $$d{height_above_wgs84}, 98 $$d{height_units}, 99 $$d{sec_since_last_dgps_update}, 100 $$d{dgps_station_id} 101 ) = split(',',shift); 102 $$d{time_utc} =~ s/(\d\d)(\d\d)(\d\d)/$1:$2:$3/g; 103 1; 104} 105 106#$GPGSA,A,2,01,,,,23,,,,,,,,2.2,2.2,*1D 107# 108 109# GPS-35: 110# $GPGSA,<1>,<2>,<3>,<3>,<3>,<3>,<3>,<3>,<3>,<3>,<3>,<3>,<3>,<3>,<4>,<5>,<6>*hh 111# 1) Mode, M=Manual, A=Automatic 112# 2) Fix type, 1=no fix, 2=2D, 3=3D 113# 3) PRN number, 01 to 32, of satellites used in solution (leading zeroes sent) 114# 4) Position dilution of precision, 1.0 to 99.9 115# 5) Horizontal dilution of precision, 1.0 to 99.9 116# 6) Vertical dilution of precision, 1.0 to 99.9 117 118sub GPGSA { 119 # GPS DOP and active satellites 120 my $self = shift; 121 $self->{NMEADATA} = {} unless ref($self->{NMEADATA}); 122 123 my $d = $self->{NMEADATA}; 124 125 (undef, 126 $$d{auto_man_D}, 127 $$d{dimen}, 128 $$d{prn01a}, 129 $$d{prn02a}, 130 $$d{prn03a}, 131 $$d{prn04a}, 132 $$d{prn05a}, 133 $$d{prn06a}, 134 $$d{prn07a}, 135 $$d{prn08a}, 136 $$d{prn09a}, 137 $$d{prn10a}, 138 $$d{prn11a}, 139 $$d{prn12a}, 140 $$d{pdop}, 141 $$d{hdop}, 142 $$d{vdop} 143 ) = split(',',shift); 144 1; 145} 146 147#$GPGSV,2,1,08,01,12,187,41,03,50,285,48,17,50,097,45,21,58,246,48*70 148# multiple lines! 149# 150 151# GPS-35: 152# $GPGSV,<1>,<2>,<3>,<4>,<5>,<6>,<7>,...<4>,<5>,<6>,<7>*hh 153# 1) Total number of GSV sentences to be transmitted 154# 2) Number of current GSV sentence 155# 3) Total number of satellites in view, 00 to 12 (leading zeros sent) 156# 4) Satellite PRN number, 01 to 32 (leading zeros sent) 157# 5) Satellite elevation, 00 to 90 degrees (leading zeros sent) 158# 6) Satellite azimuth, 000 to 359 degrees, true (leading zeros sent) 159# 7) Signal to Noise ratio (C/No) 00 to 99 dB, null when not tracking (leading zeros sent) 160sub GPGSV { 161 # Satellites in view 162 163 my $self = shift; 164 $self->{NMEADATA} = {} unless ref($self->{NMEADATA}); 165 my $d = $self->{NMEADATA}; 166 167 my @data = split(',',shift); 168 my $sentence = $data[2]; 169 170 if ($sentence == 1) { 171 (undef, 172 $$d{num_sentences}, 173 $$d{sentence}, 174 $$d{num_sat_vis}, 175 $$d{prn01}, 176 $$d{elev_deg1}, 177 $$d{az_deg1}, 178 $$d{sig_str1}, 179 $$d{prn02}, 180 $$d{elev_deg2}, 181 $$d{az_deg2}, 182 $$d{sig_str2}, 183 $$d{prn03}, 184 $$d{elev_deg3}, 185 $$d{az_deg3}, 186 $$d{sig_str3}, 187 $$d{prn04}, 188 $$d{elev_deg4}, 189 $$d{az_deg4}, 190 $$d{sig_str4} 191 ) = @data; 192 193 } elsif ($sentence == 2) { 194 (undef, 195 $$d{num_sentences}, 196 $$d{sentence}, 197 $$d{num_sat_vis}, 198 $$d{prn05}, 199 $$d{elev_deg5}, 200 $$d{az_deg5}, 201 $$d{sig_str5}, 202 $$d{prn06}, 203 $$d{elev_deg6}, 204 $$d{az_deg6}, 205 $$d{sig_str6}, 206 $$d{prn07}, 207 $$d{elev_deg7}, 208 $$d{az_deg7}, 209 $$d{sig_str7}, 210 $$d{prn08}, 211 $$d{elev_deg8}, 212 $$d{az_deg8}, 213 $$d{sig_str8} 214 ) = @data; 215 216 } elsif ($sentence == 3) { 217 (undef, 218 $$d{num_sentences}, 219 $$d{sentence}, 220 $$d{num_sat_vis}, 221 $$d{prn09}, 222 $$d{elev_deg9}, 223 $$d{az_deg9}, 224 $$d{sig_str9}, 225 $$d{prn10}, 226 $$d{elev_deg10}, 227 $$d{az_deg10}, 228 $$d{sig_str10}, 229 $$d{prn11}, 230 $$d{elev_deg11}, 231 $$d{az_deg11}, 232 $$d{sig_str11}, 233 $$d{prn12}, 234 $$d{elev_deg12}, 235 $$d{az_deg12}, 236 $$d{sig_str12} 237 ) = @data; 238 } 239 1; 240} 241 242 243#$PGRME,45.8,M,,M,156.8,M*33 244# 245 246# GPS-35: 247# $PGRME,<1>,M,<2>,M,<3>,M*hh 248# 1) Estimated horizontal position error (HPE), 0.0 to 9999.9 meters 249# 2) Estimated vertical position error (VPE), 0.0 to 9999.9 meters 250# 3) Estimated position error (EPE), 0.0 to 9999.9 meters 251sub PGRME { 252 # Estimated horiz, vertical, spherical error in meters 253 my $self = shift; 254 $self->{NMEADATA} = {} unless ref($self->{NMEADATA}); 255 my $d = $self->{NMEADATA}; 256 257 (undef, 258 $$d{hpe}, 259 $$d{hpe_units}, 260 $$d{vpe}, 261 $$d{vpe_units}, 262 $$d{overall_error}, 263 $$d{overall_error_units} 264 ) = split(",",shift); 265 1; 266} 267 268#$GPGLL,4748.811,N,12219.564,W,033850,A*3C 269# 270 271# $GPGLL,<1>,<2>,<3>,<4>,<5>, 272# 1) Latitude, ddmm.mm format 273# 2) Latitude hemisphere, N or S 274# 3) Longitude, dddmm.mm format 275# 4) Longitude hemisphere, E or W 276# 5) UTC time of position fix, hhmmss format 277# 6) Data valid, A=Valid 278sub GPGLL { 279 # Geographic position, Latitude and Longitude 280 my $self = shift; 281 $self->{NMEADATA} = {} unless ref($self->{NMEADATA}); 282 my $d = $self->{NMEADATA}; 283 284 (undef, 285 $$d{lat_ddmm_low}, 286 $$d{lat_NS}, 287 $$d{lon_ddmm_low}, 288 $$d{lon_EW}, 289 $$d{time_utc}, 290 $$d{data_valid} 291 ) = split(",",shift); 292 293 1; 294} 295 296 297 298#$PGRMZ,665,f,2*1F 299# 300# $PGRMZ,<1>,f,<2>,M*dd 301# 1) Altitude in feet 302# 2) Position fix, 2=user altitude, 3=GPS altitude 303# 304sub PGRMZ { 305 # Altitude & units, 2 = user altitude 3 = GPS altitude 306 my $self = shift; 307 $self->{NMEADATA} = {} unless ref($self->{NMEADATA}); 308 my $d = $self->{NMEADATA}; 309 310 (undef, 311 $$d{alt}, 312 $$d{alt_units}, 313 $$d{alt_mode} 314 ) = split(",",shift); 315 1; 316} 317 318 319#$PGRMM,WGS 84*06 320# 321 322sub PGRMM { 323 # Currently active horizontal datum 324 my $self = shift; 325 $self->{NMEADATA} = {} unless ref($self->{NMEADATA}); 326 my $d = $self->{NMEADATA}; 327 328 (undef, 329 $$d{datum}, 330 ) = split(",",shift); 331 1; 332} 333 334#$GPBOD,045.,T,023.,M,DEST,START*47 335# 336 337# $GPBOD,<1>,T,<2>,M,<3>,<4>*dd 338# 1) Bearing from "start" to "dest", true 339# 2) Bearing from "start" to "dest", magnetic 340# 3) Destination waypoint ID 341# 4) Origin waypoint ID 342 343sub GPBOD { 344 # Bearing - origin to destination waypoint 345 my $self = shift; 346 $self->{NMEADATA} = {} unless ref($self->{NMEADATA}); 347 my $d = $self->{NMEADATA}; 348 349 (undef, 350 $$d{bearing_start_to_dest_true}, 351 $$d{junk31}, 352 $$d{bearing_start_to_dest_mag}, 353 $$d{junk32}, 354 $$d{dest_waypoint}, 355 $$d{origin_waypoint} 356 ) = split(",",shift); 357 358 1; 359} 360 361 362#$GPRTE,2,1,c,0,W3IWI,DRIVWY,32CEDR,32-29,32BKLD,32-I95,32-US1*69 363# multiple lines! 364 365# $GPRTE,<1>,<2>,<3>,<4>,<5>,<5>,<5>...<5>*dd 366# 1) Number of GPRTE sentences 367# 2) Number of this sentence 368# 3) c=complete list of waypoints in this route, w=first listed waypoint is start of current leg 369# 4) Route identifier (0-?) 370# 5) Waypoint identifier 371sub GPRTE { 372 # Waypoints in active route 373 my $self = shift; 374 $self->{NMEADATA} = {} unless ref($self->{NMEADATA}); 375 my $d = $self->{NMEADATA}; 376 377 my @data = split(',',shift); 378 my $sentence = $data[2]; 379 380 if ($sentence == 1) { 381 (undef, 382 $$d{num_sentences}, 383 undef, 384 $$d{c_w}, 385 $$d{route_num}, 386 @{$d->{waypoint1}} 387 ) = @data; 388 undef $d->{waypoint2} if ref($d->{waypoint2}); 389 undef $d->{waypoint3} if ref($d->{waypoint3}); 390 } elsif ($sentence == 2) { 391 (undef, 392 $$d{num_sentences}, 393 undef, 394 $$d{c_w}, 395 $$d{route_num}, 396 @{$d->{waypoint2}} 397 ) = @data; 398 undef $d->{waypoint3} if ref($d->{waypoint3}); 399 } elsif ($sentence == 2) { 400 (undef, 401 $$d{num_sentences}, 402 undef, 403 $$d{c_w}, 404 $$d{route_num}, 405 @{$d->{waypoint3}} 406 ) = @data; 407 } 408 409 410 1; 411} 412 413 414#$GPRMC,033850,A,4748.811,N,12219.564,W,000.0,150.3,160596,019.6,E*64 415# 416 417# GPS-35: 418# $GPRMC,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,<10>,<11>*hh 419# 1) UTC time of position fix, hhmmss format 420# 2) Status, A=Valid position, V=NAV receiver warning 421# 3) Latitude, ddmm.mmmm format (leading zeros sent) 422# 4) Latitude hemisphere, N or S 423# 5) Longitude, dddmm.mmmm format (leading zeros sent) 424# 6) Longitude hemisphere, E or W 425# 7) Speed over ground, 0.0 to 999.9 knots 426# 8) Course over ground, 000.0 to 359.9 degrees, true (leading zeros sent) 427# 9) UTC date of position fix, ddmmyy format 428# 10) Magnetic variation, 000.0 to 180.0 degrees (leading zeros sent) 429# 11) Magnetic variation direction, E or W (westerly variation adds to true course) 430 431sub GPRMC { 432 # RMC - Recommended minimum specific GPS/Transit data 433 my $self = shift; 434 $self->{NMEADATA} = {} unless ref($self->{NMEADATA}); 435 my $d = $self->{NMEADATA}; 436 437 (undef, 438 $$d{time_utc}, 439 $$d{data_valid}, 440 $$d{lat_ddmm}, 441 $$d{lat_NS}, 442 $$d{lon_ddmm}, 443 $$d{lon_EW}, 444 $$d{speed_over_ground}, 445 $$d{course_made_good}, 446 $$d{ddmmyy}, 447 $$d{mag_var}, 448 $$d{mag_var_EW} 449 ) = split(",",shift); 450 451 $d->{time_utc} =~ s/(\d\d)(\d\d)(\d\d)/$1:$2:$3/g; 452 1; 453} 454 4551; 456 457 458__END__ 459# 460 461=head1 NAME 462 463GPS::NMEA::Handler - Handlers to NMEA data 464 465=head1 SYNOPSIS 466 467 use GPS::NMEA::Handler; 468 469 470=head1 DESCRIPTION 471 472 Used internally 473 474=over 475 476=head1 AUTHOR 477 478Based on 479NMEA Parsing Program 480Copyright (C) 1997-2000, Curt Mills and Lane Holdcroft. 481 482=head1 SEE ALSO 483 484=cut 485