ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-GPSD/GPSD.pm
(Generate patch)

Comparing AnyEvent-GPSD/GPSD.pm (file contents):
Revision 1.2 by root, Wed Jul 2 05:17:37 2008 UTC vs.
Revision 1.7 by root, Sat Jul 26 05:34:58 2008 UTC

57 57
58=item port => $port 58=item port => $port
59 59
60The port to connect to, default is C<2947>. 60The port to connect to, default is C<2947>.
61 61
62=item min_speed => $speed_in_m_per_s
63
64Sets the mininum speed (default: 0) that is considered real for the
65purposes of replay compression or estimate. Speeds below this value will
66be considered 0.
67
62=item on_error => $cb->($gps) 68=item on_error => $cb->($gps)
63 69
64Called on every connection or protocol failure, reason is in C<$!> 70Called on every connection or protocol failure, reason is in C<$!>
65(protocl errors are signalled via EBADMSG). Can be used to bail out if you 71(protocl errors are signalled via EBADMSG). Can be used to bail out if you
66are not interested in retries. 72are not interested in retries.
88 94
89C<snr> contains the signal strength in decibals (28+ is usually the 95C<snr> contains the signal strength in decibals (28+ is usually the
90minimum value for a good fix). 96minimum value for a good fix).
91 97
92C<fix> contains either C<1> to indicate that this satellite was used for 98C<fix> contains either C<1> to indicate that this satellite was used for
93the last position fix, C<0> otherwise. EGNOS/WAAS etc. satellites will 99the last position fix, C<0> otherwise. EGNOS/WAAS etc. satellites will
94always show as C<0>, even if their correction info was used. 100always show as C<0>, even if their correction info was used.
101
102The passed hash references are read-only.
95 103
96=item on_fix => $cb->({point}) 104=item on_fix => $cb->({point})
97 105
98Called regularly (usually about once/second), even when there is no 106Called regularly (usually about once/second), even when there is no
99connection to the GPSD (so is useful to update your idea of the current 107connection to the GPSD (so is useful to update your idea of the current
138 $self->connect; 146 $self->connect;
139 147
140 $self 148 $self
141} 149}
142 150
151sub DESTROY {
152 my ($self) = @_;
153
154 $self->record_log;
155}
156
143sub event { 157sub event {
144 my $event = splice @_, 1, 1, (); 158 my $event = splice @_, 1, 1, ();
145 159
146 warn "event<$event,@_>\n";#d# 160 #warn "event<$event,@_>\n";#d#
147 if ($event = $_[0]{"on_$event"}) { 161 if ($event = $_[0]{"on_$event"}) {
148 &$event; 162 &$event;
149 } 163 }
150} 164}
151 165
152sub retry { 166sub retry {
153 my ($self) = @_; 167 my ($self) = @_;
154 168
155 delete $self->{fh}; 169 delete $self->{fh};
170 delete $self->{command};
156 171
157 Scalar::Util::weaken $self; 172 Scalar::Util::weaken $self;
158 $self->{retry_w} = AnyEvent->timer (after => 1, cb => sub { 173 $self->{retry_w} = AnyEvent->timer (after => 1, cb => sub {
159 delete $self->{retry_w}; 174 delete $self->{retry_w};
160 $self->connect; 175 $self->connect;
204 $self->retry; 219 $self->retry;
205 }, 220 },
206 on_eof => sub { 221 on_eof => sub {
207 $! = &Errno::EPIPE; 222 $! = &Errno::EPIPE;
208 $self->event ("error"); 223 $self->event ("error");
224 $self->log ("disconnect");
209 $self->retry; 225 $self->retry;
210 }, 226 },
211 on_read => sub { 227 on_read => sub {
212 $_[0]{rbuf} =~ s/^([^\015\012]*)\015\012// 228 $_[0]{rbuf} =~ s/^([^\015\012]*)\015\012//
213 or return; 229 or return;
214 230
215 $self->feed ($1); 231 $self->feed ($1)
232 unless $self->{replay_cb};
216 }, 233 },
217 ; 234 ;
218 235
219 $self->send ("w"); 236 $self->send ("w");
220 $self->send ("o"); 237 $self->send ("o");
221 $self->send ("y"); 238 $self->send ("y");
222 $self->send ("c"); 239 $self->send ("c");
223 240
224 $self->event ("connect"); 241 $self->event ("connect");
242 $self->log ("connect");
225 } else { 243 } else {
226 $self->event ("error"); 244 $self->event ("error");
227 } 245 }
228 }; 246 };
229 247
249sub feed { 267sub feed {
250 my ($self, $line) = @_; 268 my ($self, $line) = @_;
251 269
252 $self->{now} = AnyEvent->now; 270 $self->{now} = AnyEvent->now;
253 271
272 $self->log (raw => $line)
273 if $self->{logfh};
274
254 unless ($line =~ /^GPSD,(.)=(.*)$/) { 275 unless ($line =~ /^GPSD,(.)=(.*)$/) {
255 $! = &Errno::EBADMSG; 276 $! = &Errno::EBADMSG;
256 $self->event ("error"); 277 $self->event ("error");
257 return $self->retry; 278 return $self->retry;
258 } 279 }
259 280
260 my ($type, $data) = ($1, $2); 281 my ($type, $data) = ($1, $2);
282
283 #warn "$type=$data\n";#d#
261 284
262 $self->{state}{$type} = [$data => $self->{now}]; 285 $self->{state}{$type} = [$data => $self->{now}];
263 286
264 if ($type eq "O") { 287 if ($type eq "O") {
265 my @data = split /\s+/, $data; 288 my @data = split /\s+/, $data;
271 if (@data > 3) { 294 if (@data > 3) {
272 # the gpsd time is virtually useless as it is truncated :/ 295 # the gpsd time is virtually useless as it is truncated :/
273 for (qw(tag _time _terr lat lon alt herr verr bearing speed vspeed berr serr vserr mode)) { 296 for (qw(tag _time _terr lat lon alt herr verr bearing speed vspeed berr serr vserr mode)) {
274 $type = shift @data; 297 $type = shift @data;
275 $fix->{$_} = $type eq "?" ? undef : $type; 298 $fix->{$_} = $type eq "?" ? undef : $type;
299 }
300
301 if (my $s = $self->{stretch}) {
302 $s = 1 / $s;
303
304 $fix->{herr} *= $s; # ?
305 $fix->{verr} *= $s; # ?
306 $fix->{berr} *= $s; # ?
307 $fix->{serr} *= $s; # ?
308 $fix->{vserr} *= $s; # ?
309
310 $fix->{speed} *= $s;
311 $fix->{vspeed} *= $s;
276 } 312 }
277 313
278 $fix->{mode} = 2 if $fix->{mode} eq "?"; # arbitrary choice 314 $fix->{mode} = 2 if $fix->{mode} eq "?"; # arbitrary choice
279 } else { 315 } else {
280 $fix->{mode} = 1; 316 $fix->{mode} = 1;
312} 348}
313 349
314=item ($lat, $lon) = $gps->estimate ([$max_seconds]) 350=item ($lat, $lon) = $gps->estimate ([$max_seconds])
315 351
316This returns an estimate of the current position based on the last fix and 352This returns an estimate of the current position based on the last fix and
317the time passed since then. Useful for interactive applications where you 353the time passed since then.
318want more frequent updates, but not very useful to store, as the next fix
319might well be totally off.
320 354
355Useful for interactive applications where you want more frequent updates,
356but not very useful to store, as the next fix might well be totally
357off. For example, when displaying a real-time map, you could simply call
358C<estimate> ten times a second and update the cursor or map position, but
359you should use C<on_fix> to actually gather data to plot the course itself.
360
321If the fix is older then C<$max_seconds> (default: C<1.9>) or if no fix is 361If the fix is older then C<$max_seconds> (default: C<1.9> times the update
322available, returns the empty list. 362interval, i.e. usually C<1.9> seconds) or if no fix is available, returns
363the empty list.
323 364
324=cut 365=cut
325 366
326sub estimate { 367sub estimate {
327 my ($self, $max) = @_; 368 my ($self, $max) = @_;
328 369
329 $max ||= 1.9 unless defined $max; 370 $max ||= 1.9 * $self->{interval} unless defined $max;
330 371
331 my $geo = $self->{geo_forward} ||= new Geo::Forward; 372 my $geo = $self->{geo_forward} ||= new Geo::Forward;
332 373
333 my $fix = $self->{fix} or return; 374 my $fix = $self->{fix} or return;
334 $fix->{mode} >= 2 or return; 375 $fix->{mode} >= 2 or return;
335 376
336 my $diff = AnyEvent->time - $fix->{time}; 377 my $diff = AnyEvent->time - $fix->{time};
337 378
338 $diff <= $max or return; 379 $diff <= $max or return;
339 380
340 if ($fix->{speed} > $fix->{serr}) { 381 if ($fix->{speed} >= $self->{min_speed}) {
341 my ($lat, $lon) = $geo->forward ($fix->{lat}, $fix->{lon}, $fix->{bearing}, $fix->{speed} * $diff); 382 my ($lat, $lon) = $geo->forward ($fix->{lat}, $fix->{lon}, $fix->{bearing}, $fix->{speed} * $diff);
342 ($lat, $lon) 383 ($lat, $lon)
343 384
344 } else { 385 } else {
345 # if we likely have zero speed, return the point itself 386 # if we likely have zero speed, return the point itself
346 ($fix->{lat}, $fix->{lon}) 387 ($fix->{lat}, $fix->{lon})
347 } 388 }
348} 389}
349 390
391sub log {
392 my ($self, @arg) = @_;
393
394 syswrite $self->{logfh}, JSON::encode_json ([AnyEvent->time, @arg]) . "\n"
395 if $self->{logfh};
396}
397
398=item $gps->record_log ($path)
399
400If C<$path> is defined, then that file will be created or truncated and a
401log of all (raw) packets received will be written to it. This log file can
402later be replayed by calling C<< $gps->replay_log ($path) >>.
403
404If C<$path> is undefined then the log will be closed.
405
406=cut
407
408sub record_log {
409 my ($self, $path) = @_;
410
411 if (defined $path) {
412 $self->record_log;
413
414 require JSON;
415
416 open $self->{logfh}, ">:perlio", $path
417 or Carp::croak "$path: $!";
418
419 $self->log (start => $VERSION, 0, 0, { interval => $self->{interval} });
420 } elsif ($self->{logfh}) {
421 $self->log ("stop");
422 delete $self->{logfh};
423 }
424}
425
426=item $gps->replay_log ($path, %options)
427
428Replays a log file written using C<record_log> (or stops replaying when
429C<$path> is undefined). While the log file replays, real GPS events will
430be ignored. This comes in handy when testing.
431
432Please note that replaying a log will change configuration options that
433will not be restored, so it's best not to reuse a gpsd object after a
434replay.
435
436The C<AnyEvent::GPSD> distribution comes with an example log
437(F<eg/example.aegps>) that you can replay for testing or enjoyment
438purposes.
439
440The options include:
441
442=over 4
443
444=item compress => 1
445
446If set to a true value (default: false), then passages without fix will be
447replayed much faster than passages with fix. The same happens for passages
448without much movement.
449
450=item stretch => $factor
451
452Multiplies all times by the given factor. Values < 1 make the log replay
453faster, values > 1 slower. Note that the frequency of fixes will not be
454increased, o stretch factors > 1 do not work well.
455
456A stretch factor of zero is not allowed, but if you want to replay a log
457instantly you may speicfy a very low value (e.g. 1e-10).
458
459=back
460
461=cut
462
463sub replay_log {
464 my ($self, $path, %option) = @_;
465
466 if (defined $path) {
467 $self->replay_log;
468
469 require JSON;
470
471 open my $fh, "<:perlio", $path
472 or Carp::croak "$path: $!";
473
474 $self->{stretch} = $option{stretch} || 1;
475 $self->{compress} = $option{compress};
476
477 $self->{imterval} /= $self->{stretch};
478
479 Scalar::Util::weaken $self;
480
481 $self->{replay_cb} = sub {
482 my $line = <$fh>;
483
484 if (2 > length $line) {
485 $self->replay_log;
486 } else {
487 my ($time, $type, @data) = @{ JSON::decode_json ($line) };
488
489 $time *= $self->{stretch};
490
491 if ($type eq "start") {
492 my ($module_version, $major_version, $minor_version, $args) = @data;
493
494 $self->{interval} = ($args->{interval} || 1) / $self->{stretch};
495 }
496
497 if (
498 $type eq "start"
499 or ($self->{compress}
500 and $self->{fix} && ($self->{fix}{mode} < 2 || $self->{fix}{speed} < $self->{min_speed}))
501 ) {
502 $self->{replay_now} = $time;
503 }
504
505 $self->{replay_timer} = AnyEvent->timer (after => $time - $self->{replay_now}, cb => sub {
506 $self->{replay_now} = $time;
507 $self->{command} = []; # no can do
508 $self->feed ($data[0]) if $type eq "raw";
509 $self->{replay_cb}();
510 });
511 }
512 };
513
514 $self->{replay_cb}();
515
516 } else {
517 delete $self->{stretch};
518 delete $self->{compress};
519 delete $self->{replay_timer};
520 delete $self->{replay_cb};
521 }
522}
523
350=back 524=back
351 525
352=head1 SEE ALSO 526=head1 SEE ALSO
353 527
354L<AnyEvent>. 528L<AnyEvent>.

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines