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.3 by root, Wed Jul 2 05:21:58 2008 UTC vs.
Revision 1.7 by root, Sat Jul 26 05:34:58 2008 UTC

56The host to connect to, default is C<locahost>. 56The host to connect to, default is C<locahost>.
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
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.
61 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
140 $self->connect; 146 $self->connect;
141 147
142 $self 148 $self
143} 149}
144 150
151sub DESTROY {
152 my ($self) = @_;
153
154 $self->record_log;
155}
156
145sub event { 157sub event {
146 my $event = splice @_, 1, 1, (); 158 my $event = splice @_, 1, 1, ();
147 159
148 warn "event<$event,@_>\n";#d# 160 #warn "event<$event,@_>\n";#d#
149 if ($event = $_[0]{"on_$event"}) { 161 if ($event = $_[0]{"on_$event"}) {
150 &$event; 162 &$event;
151 } 163 }
152} 164}
153 165
154sub retry { 166sub retry {
155 my ($self) = @_; 167 my ($self) = @_;
156 168
157 delete $self->{fh}; 169 delete $self->{fh};
170 delete $self->{command};
158 171
159 Scalar::Util::weaken $self; 172 Scalar::Util::weaken $self;
160 $self->{retry_w} = AnyEvent->timer (after => 1, cb => sub { 173 $self->{retry_w} = AnyEvent->timer (after => 1, cb => sub {
161 delete $self->{retry_w}; 174 delete $self->{retry_w};
162 $self->connect; 175 $self->connect;
206 $self->retry; 219 $self->retry;
207 }, 220 },
208 on_eof => sub { 221 on_eof => sub {
209 $! = &Errno::EPIPE; 222 $! = &Errno::EPIPE;
210 $self->event ("error"); 223 $self->event ("error");
224 $self->log ("disconnect");
211 $self->retry; 225 $self->retry;
212 }, 226 },
213 on_read => sub { 227 on_read => sub {
214 $_[0]{rbuf} =~ s/^([^\015\012]*)\015\012// 228 $_[0]{rbuf} =~ s/^([^\015\012]*)\015\012//
215 or return; 229 or return;
216 230
217 $self->feed ($1); 231 $self->feed ($1)
232 unless $self->{replay_cb};
218 }, 233 },
219 ; 234 ;
220 235
221 $self->send ("w"); 236 $self->send ("w");
222 $self->send ("o"); 237 $self->send ("o");
223 $self->send ("y"); 238 $self->send ("y");
224 $self->send ("c"); 239 $self->send ("c");
225 240
226 $self->event ("connect"); 241 $self->event ("connect");
242 $self->log ("connect");
227 } else { 243 } else {
228 $self->event ("error"); 244 $self->event ("error");
229 } 245 }
230 }; 246 };
231 247
251sub feed { 267sub feed {
252 my ($self, $line) = @_; 268 my ($self, $line) = @_;
253 269
254 $self->{now} = AnyEvent->now; 270 $self->{now} = AnyEvent->now;
255 271
272 $self->log (raw => $line)
273 if $self->{logfh};
274
256 unless ($line =~ /^GPSD,(.)=(.*)$/) { 275 unless ($line =~ /^GPSD,(.)=(.*)$/) {
257 $! = &Errno::EBADMSG; 276 $! = &Errno::EBADMSG;
258 $self->event ("error"); 277 $self->event ("error");
259 return $self->retry; 278 return $self->retry;
260 } 279 }
261 280
262 my ($type, $data) = ($1, $2); 281 my ($type, $data) = ($1, $2);
282
283 #warn "$type=$data\n";#d#
263 284
264 $self->{state}{$type} = [$data => $self->{now}]; 285 $self->{state}{$type} = [$data => $self->{now}];
265 286
266 if ($type eq "O") { 287 if ($type eq "O") {
267 my @data = split /\s+/, $data; 288 my @data = split /\s+/, $data;
273 if (@data > 3) { 294 if (@data > 3) {
274 # the gpsd time is virtually useless as it is truncated :/ 295 # the gpsd time is virtually useless as it is truncated :/
275 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)) {
276 $type = shift @data; 297 $type = shift @data;
277 $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;
278 } 312 }
279 313
280 $fix->{mode} = 2 if $fix->{mode} eq "?"; # arbitrary choice 314 $fix->{mode} = 2 if $fix->{mode} eq "?"; # arbitrary choice
281 } else { 315 } else {
282 $fix->{mode} = 1; 316 $fix->{mode} = 1;
314} 348}
315 349
316=item ($lat, $lon) = $gps->estimate ([$max_seconds]) 350=item ($lat, $lon) = $gps->estimate ([$max_seconds])
317 351
318This 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
319the time passed since then. Useful for interactive applications where you 353the time passed since then.
320want more frequent updates, but not very useful to store, as the next fix 354
321might well be totally off. 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.
322 360
323If the fix is older then C<$max_seconds> (default: C<1.9> times the update 361If the fix is older then C<$max_seconds> (default: C<1.9> times the update
324interval, i.e. usually C<1.9> seconds) or if no fix is available, returns 362interval, i.e. usually C<1.9> seconds) or if no fix is available, returns
325the empty list. 363the empty list.
326 364
338 376
339 my $diff = AnyEvent->time - $fix->{time}; 377 my $diff = AnyEvent->time - $fix->{time};
340 378
341 $diff <= $max or return; 379 $diff <= $max or return;
342 380
343 if ($fix->{speed} > $fix->{serr}) { 381 if ($fix->{speed} >= $self->{min_speed}) {
344 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);
345 ($lat, $lon) 383 ($lat, $lon)
346 384
347 } else { 385 } else {
348 # if we likely have zero speed, return the point itself 386 # if we likely have zero speed, return the point itself
349 ($fix->{lat}, $fix->{lon}) 387 ($fix->{lat}, $fix->{lon})
350 } 388 }
351} 389}
352 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
353=back 524=back
354 525
355=head1 SEE ALSO 526=head1 SEE ALSO
356 527
357L<AnyEvent>. 528L<AnyEvent>.

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines