File Coverage

blib/lib/Net/GPSD.pm
Criterion Covered Total %
statement 67 158 42.4
branch 5 40 12.5
condition 5 20 25.0
subroutine 14 31 45.1
pod 24 24 100.0
total 115 273 42.1


line stmt bran cond sub pod time code
1             package Net::GPSD;
2 1     1   643 use strict;
  1         1  
  1         30  
3 1     1   4 use warnings;
  1         2  
  1         28  
4 1     1   786 use IO::Socket::INET;
  1         23857  
  1         8  
5 1     1   1258 use Net::GPSD::Point;
  1         3  
  1         30  
6 1     1   615 use Net::GPSD::Satellite;
  1         2  
  1         1472  
7              
8             our $VERSION='0.39';
9              
10             =head1 NAME
11              
12             Net::GPSD - Provides an object client interface to the gpsd server daemon.
13              
14             =head1 SYNOPSIS
15              
16             use Net::GPSD;
17             $obj=Net::GPSD->new;
18             my $point=$obj->get;
19             print $point->latlon. "\n";
20              
21             or
22              
23             use Net::GPSD;
24             $obj=Net::GPSD->new;
25             $obj->subscribe();
26              
27             =head1 DESCRIPTION
28              
29             Note: This package supports the older version 2 protocol. It works for gpsd versions less than 3.00. However, for all versions of the gpsd deamon greater than 2.90 you should use the version 3 protocol supported by L.
30              
31             Net::GPSD provides an object client interface to the gpsd server daemon. gpsd is an open source GPS deamon from http://gpsd.berlios.de/.
32              
33             For example the get method returns a blessed hash reference like
34              
35             {S=>[?|0|1|2],
36             P=>[lat,lon]}
37              
38             Fortunately, there are various methods that hide this hash from the user.
39              
40             =head1 CONSTRUCTOR
41              
42             =head2 new
43              
44             Returns a new Net::GPSD object.
45              
46             my $obj=Net::GPSD->new(host=>"localhost", port=>"2947");
47              
48             =cut
49              
50             sub new {
51 1     1 1 107 my $this = shift();
52 1   33     12 my $class = ref($this) || $this;
53 1         3 my $self = {};
54 1         4 bless $self, $class;
55 1         4 $self->initialize(@_);
56 1         3 return $self;
57             }
58              
59             =head1 METHODS
60              
61             =head2 initialize
62              
63             =cut
64              
65             sub initialize {
66 1     1 1 2 my $self = shift();
67 1         4 my %param = @_;
68 1   50     15 $self->{'host'}=$param{'host'} || 'localhost';
69 1   50     8 $self->{'port'}=$param{'port'} || '2947';
70 1 50       6 unless ($param{'do_not_init'}) { #for testing
71 0         0 my $data=$self->retrieve('LKIFCB');
72 0         0 foreach (keys %$data) {
73 0         0 $self->{$_}=[@{$data->{$_}}]; #there has got to be a better way to do this...
  0         0  
74             }
75             }
76             }
77              
78             =head2 get
79              
80             Returns a current point object regardless if there is a fix or not. Applications should test if $point->fix is true.
81              
82             my $point=$obj->get();
83              
84             =cut
85              
86             sub get {
87 0     0 1 0 my $self=shift();
88 0         0 my $data=$self->retrieve('SMDO');
89 0         0 return Net::GPSD::Point->new($data);
90             }
91              
92             =head2 subscribe
93              
94             The subscribe method listens to gpsd server in watcher (W command) mode and calls the handler for each point received. The return for the handler will be sent back as the first argument to the handler on the next call.
95              
96             $obj->subscribe();
97             $obj->subscribe(handler=>\&gpsd_handler, config=>$config);
98              
99             =cut
100              
101             sub subscribe {
102 0     0 1 0 my $self = shift();
103 0         0 my %param = @_;
104 0         0 my $last=undef();
105 0   0     0 my $handler=$param{'handler'} || \&default_point_handler;
106 0   0     0 my $satlisthandler=$param{'satlisthandler'} || \&default_satellitelist_handler;
107 0   0     0 my $config=$param{'config'} || {};
108 0         0 my $sock = IO::Socket::INET->new(PeerAddr=>$self->host,
109             PeerPort=>$self->port);
110 0         0 $sock->send("W\n");
111 0         0 my $data;
112             my $point;
113 0         0 while (defined($_=$sock->getline)) {
114 0 0       0 if (m/,O=/) {
    0          
    0          
    0          
115 0         0 $point=Net::GPSD::Point->new($self->parse($_));
116 0 0       0 $point->mode(defined($point->tag) ? (defined($point->alt) ? 3 : 2) : 0);
    0          
117 0 0       0 if ($point->fix) {
118 0         0 my $return=&{$handler}($last, $point, $config);
  0         0  
119 0 0       0 $last=$return if (defined($return));
120             }
121             } elsif (m/,W=/) {
122             } elsif (m/,Y=/) {
123             } elsif (m/,X=/) {
124             } else {
125 0         0 warn "Unknown: $_\n";
126             }
127             }
128             }
129              
130             =head2 default_point_handler
131              
132             =cut
133              
134             sub default_point_handler {
135 0     0 1 0 my $p1=shift(); #last return or undef if first
136 0         0 my $p2=shift(); #current fix
137 0         0 my $config=shift(); #configuration data
138 0         0 print $p2->latlon. "\n";
139 0         0 return $p2;
140             }
141              
142             =head2 default_satellitelist_handler
143              
144             =cut
145              
146             sub default_satellitelist_handler {
147 0     0 1 0 my $sl=shift();
148 0         0 my $i=0;
149 0         0 print join("\t", qw{Count PRN ELEV Azim SNR USED}), "\n";
150 0         0 foreach (@$sl) {
151 0         0 print join "\t", ++$i,
152             $_->prn,
153             $_->elev,
154             $_->azim,
155             $_->snr,
156             $_->used;
157 0         0 print "\n";
158             }
159 0         0 return 1;
160             }
161              
162             =head2 getsatellitelist
163              
164             Returns a list of Net::GPSD::Satellite objects. (maps to gpsd Y command)
165              
166             my @list=$obj->getsatellitelist;
167             my $list=$obj->getsatellitelist;
168              
169             =cut
170              
171             sub getsatellitelist {
172 0     0 1 0 my $self=shift();
173 0         0 my $string='Y';
174 0         0 my $data=$self->retrieve($string);
175 0         0 my @data = @{$data->{'Y'}};
  0         0  
176 0         0 shift(@data); #Drop sentence tag
177 0         0 my @list = ();
178 0         0 foreach (@data) {
179             #print "$_\n";
180 0         0 push @list, Net::GPSD::Satellite->new(split " ", $_);
181             }
182 0 0       0 return wantarray ? @list : \@list;
183             }
184              
185             =head2 retrieve
186              
187             =cut
188              
189             sub retrieve {
190 0     0 1 0 my $self=shift();
191 0         0 my $string=shift();
192 0         0 my $sock=$self->open();
193 0 0       0 if (defined($sock)) {
194 0 0       0 $sock->send($string) or die("Error: $!");
195 0         0 my $data=$sock->getline;
196 0         0 chomp $data;
197 0         0 return $self->parse($data);
198             } else {
199 0         0 warn "$0: Could not connect to gspd host.\n";
200 0         0 return undef();
201             }
202             }
203              
204             =head2 open
205              
206             =cut
207              
208             sub open {
209 0     0 1 0 my $self=shift();
210 0 0 0     0 if (! defined($self->{'sock'}) || ! defined($self->{'sock'}->connected())) {
211 0         0 $self->{'sock'} = IO::Socket::INET->new(PeerAddr => $self->host,
212             PeerPort => $self->port);
213             }
214 0         0 return $self->{'sock'};
215             }
216              
217             =head2 parse
218              
219             =cut
220              
221             sub parse {
222 0     0 1 0 my $self=shift();
223 0         0 my $line=shift();
224 0         0 my %data=();
225 0         0 my @line=split(/[,\n\r]/, $line);
226 0         0 foreach (@line) {
227 0 0       0 if (m/(.*)=(.*)/) {
228 0 0       0 if ($1 eq 'Y') {
229 0         0 $data{$1}=[split(/:/, $2)]; #Y is : delimited
230             } else {
231 0 0       0 $data{$1}=[map {$_ eq '?' ? undef() : $_} split(/\s+/, $2)];
  0         0  
232             }
233             }
234             }
235 0         0 return \%data;
236             }
237              
238             =head2 host
239              
240             Sets or returns the current gpsd host.
241              
242             my $host=$obj->host;
243              
244             =cut
245              
246             sub host {
247 3     3 1 43 my $self = shift();
248 3 100       10 if (@_) {
249 1         2 $self->{'host'} = shift();
250             }
251 3         32 return $self->{'host'};
252             }
253              
254             =head2 port
255              
256             Sets or returns the current gpsd TCP port.
257              
258             my $port=$obj->port;
259              
260             =cut
261              
262             sub port {
263 5     5 1 9 my $self = shift();
264 5 100       10 if (@_) {
265 2         5 $self->{'port'} = shift();
266             }
267 5         13 return $self->{'port'};
268             }
269              
270             =head2 baud
271              
272             Returns the baud rate of the connect GPS receiver. (maps to gpsd B command first data element)
273              
274             my $baud=$obj->baud;
275              
276             =cut
277              
278             sub baud {
279 0     0 1 0 my $self = shift();
280 0         0 return q2u $self->{'B'}->[0];
281             }
282              
283             =head2 rate
284              
285             Returns the sampling rate of the GPS receiver. (maps to gpsd C command first data element)
286              
287             my $rate=$obj->rate;
288              
289             =cut
290              
291             sub rate {
292 0     0 1 0 my $self = shift();
293 0         0 return q2u $self->{'C'}->[0];
294             }
295              
296             =head2 device
297              
298             Returns the GPS device name. (maps to gpsd F command first data element)
299              
300             my $device=$obj->device;
301              
302             =cut
303              
304             sub device {
305 0     0 1 0 my $self = shift();
306 0         0 return q2u $self->{'F'}->[0];
307             }
308              
309             =head2 identification (aka id)
310              
311             Returns a text string identifying the GPS. (maps to gpsd I command first data element)
312              
313             my $identification=$obj->identification;
314             my $identification=$obj->id;
315              
316             =cut
317              
318             sub identification {
319 0     0 1 0 my $self = shift();
320 0         0 return q2u $self->{'I'}->[0];
321             }
322              
323             =head2 id
324              
325             =cut
326              
327             sub id {
328 0     0 1 0 my $self = shift();
329 0         0 return $self->identification;
330             }
331              
332             =head2 protocol
333              
334             Returns the GPSD protocol revision number. (maps to gpsd L command first data element)
335              
336             my $protocol=$obj->protocol;
337              
338             =cut
339              
340             sub protocol {
341 0     0 1 0 my $self = shift();
342 0         0 return q2u $self->{'L'}->[0];
343             }
344              
345             =head2 daemon
346              
347             Returns the gpsd daemon version. (maps to gpsd L command second data element)
348              
349             my $daemon=$obj->daemon;
350              
351             =cut
352              
353             sub daemon {
354 0     0 1 0 my $self = shift();
355 0         0 return q2u $self->{'L'}->[1];
356             }
357              
358             =head2 commands
359              
360             Returns a string of accepted command letters. (maps to gpsd L command third data element)
361              
362             my $commands=$obj->commands;
363              
364             =cut
365              
366             sub commands {
367 0     0 1 0 my $self = shift();
368 0         0 my $string=q2u $self->{'L'}->[2];
369 0 0       0 return wantarray ? split(//, $string) : $string
370             }
371              
372             =head1 FUNCTIONS
373              
374             =head2 time
375              
376             Returns the time difference between two point objects in seconds.
377              
378             my $seconds=$obj->time($p1, $p2);
379              
380             =cut
381              
382             sub time {
383             #seconds between p1 and p2
384 2     2 1 3 my $self=shift();
385 2         3 my $p1=shift();
386 2         3 my $p2=shift();
387 2         5 return abs($p2->time - $p1->time);
388             }
389              
390             =head2 distance
391              
392             Returns the distance difference between two point objects in meters. (simple calculation)
393              
394             my $meters=$obj->distance($p1, $p2);
395              
396             =cut
397              
398             sub distance {
399             #returns meters between p1 and p2
400 2     2 1 8 my $self=shift();
401 2         3 my $p1=shift();
402 2         2 my $p2=shift();
403 2         6 my $lat1=$p1->lat;
404 2         7 my $lon1=$p1->lon;
405 2         5 my $lon2=$p2->lon;
406 2         6 my $lat2=$p2->lat;
407              
408 1     1   677 use Geo::Inverse;
  1         3676  
  1         92  
409 2         11 my $obj = Geo::Inverse->new();
410 2         530 my ($faz, $baz, $dist)=$obj->inverse($lat1, $lon1, $lat2, $lon2);
411 2         387 return $dist;
412             }
413              
414             =head2 track
415              
416             Returns a point object at the predicted location in time seconds assuming constant velocity. (Geo::Forward calculation)
417              
418             my $point=$obj->track($p1, $seconds);
419              
420             =cut
421              
422             sub track {
423             #return calculated point of $p1 in time assuming constant velocity
424 1     1 1 39 my $self=shift();
425 1         2 my $p1=shift();
426 1         2 my $time=shift();
427 1     1   732 use Geo::Forward;
  1         1761  
  1         168  
428 1         13 my $object = Geo::Forward->new(); # default "WGS84"
429 1   50     269 my $dist=($p1->speed||0) * $time; #meters
430 1   50     5 my ($lat1,$lon1,$faz)=($p1->lat, $p1->lon, $p1->heading||0);
431 1         6 my ($lat2,$lon2,$baz) = $object->forward($lat1,$lon1,$faz,$dist);
432              
433 1         153 my $p2=Net::GPSD::Point->new($p1);
434 1         4 $p2->lat($lat2);
435 1         4 $p2->lon($lon2);
436 1         4 $p2->time($p1->time + $time);
437 1         6 $p2->heading($baz-180);
438 1         13 return $p2;
439             }
440              
441             =head2 q2u
442              
443             =cut
444              
445             sub q2u {
446 0     0 1   my $a=shift();
447 0 0         return $a eq '?' ? undef() : $a;
448             }
449              
450             =head1 GETTING STARTED
451              
452             Try the examples in the bin folder. Most every method has a default which is most likely what you will want.
453              
454             =head1 LIMITATIONS
455              
456             The distance function is Geo::Inverse.
457              
458             The track function uses Geo::Forward.
459              
460             All units are degrees, meters, seconds.
461              
462             =head1 BUGS
463              
464             Email the author and log on RT.
465              
466             =head1 EXAMPLES
467              
468             =begin html
469              
470            
471            
  • example-get.pl
  • 472            
  • example-subscribe.pl
  • 473            
  • example-subscribe-handler.pl
  • 474            
  • example-check.pl
  • 475            
  • example-information.pl
  • 476            
  • example-getsatellitelist.pl
  • 477            
  • example-tracker.pl
  • 478            
  • example-tracker-http.pl
  • 479            
  • example-tracker-text.pl
  • 480            
    481              
    482             =end html
    483              
    484             =head1 SUPPORT
    485              
    486             DavisNetworks.com supports all Perl applications including this package.
    487              
    488             =head1 AUTHOR
    489              
    490             Michael R. Davis, qw/gpsd michaelrdavis com/
    491              
    492             =head1 LICENSE
    493              
    494             Copyright (c) 2006 Michael R. Davis (mrdvt92)
    495              
    496             This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
    497              
    498             =head1 SEE ALSO
    499              
    500             L, L
    501              
    502             =cut
    503              
    504             1;