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