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.6 by root, Fri Jul 25 13:26:06 2008 UTC

140 $self->connect; 140 $self->connect;
141 141
142 $self 142 $self
143} 143}
144 144
145sub DESTROY {
146 my ($self) = @_;
147
148 $self->record_log;
149}
150
145sub event { 151sub event {
146 my $event = splice @_, 1, 1, (); 152 my $event = splice @_, 1, 1, ();
147 153
148 warn "event<$event,@_>\n";#d# 154 #warn "event<$event,@_>\n";#d#
149 if ($event = $_[0]{"on_$event"}) { 155 if ($event = $_[0]{"on_$event"}) {
150 &$event; 156 &$event;
151 } 157 }
152} 158}
153 159
154sub retry { 160sub retry {
155 my ($self) = @_; 161 my ($self) = @_;
156 162
157 delete $self->{fh}; 163 delete $self->{fh};
164 delete $self->{command};
158 165
159 Scalar::Util::weaken $self; 166 Scalar::Util::weaken $self;
160 $self->{retry_w} = AnyEvent->timer (after => 1, cb => sub { 167 $self->{retry_w} = AnyEvent->timer (after => 1, cb => sub {
161 delete $self->{retry_w}; 168 delete $self->{retry_w};
162 $self->connect; 169 $self->connect;
206 $self->retry; 213 $self->retry;
207 }, 214 },
208 on_eof => sub { 215 on_eof => sub {
209 $! = &Errno::EPIPE; 216 $! = &Errno::EPIPE;
210 $self->event ("error"); 217 $self->event ("error");
218 $self->log ("disconnect");
211 $self->retry; 219 $self->retry;
212 }, 220 },
213 on_read => sub { 221 on_read => sub {
214 $_[0]{rbuf} =~ s/^([^\015\012]*)\015\012// 222 $_[0]{rbuf} =~ s/^([^\015\012]*)\015\012//
215 or return; 223 or return;
216 224
217 $self->feed ($1); 225 $self->feed ($1)
226 unless $self->{replay_cb};
218 }, 227 },
219 ; 228 ;
220 229
221 $self->send ("w"); 230 $self->send ("w");
222 $self->send ("o"); 231 $self->send ("o");
223 $self->send ("y"); 232 $self->send ("y");
224 $self->send ("c"); 233 $self->send ("c");
225 234
226 $self->event ("connect"); 235 $self->event ("connect");
236 $self->log ("connect");
227 } else { 237 } else {
228 $self->event ("error"); 238 $self->event ("error");
229 } 239 }
230 }; 240 };
231 241
251sub feed { 261sub feed {
252 my ($self, $line) = @_; 262 my ($self, $line) = @_;
253 263
254 $self->{now} = AnyEvent->now; 264 $self->{now} = AnyEvent->now;
255 265
266 $self->log (raw => $line)
267 if $self->{logfh};
268
256 unless ($line =~ /^GPSD,(.)=(.*)$/) { 269 unless ($line =~ /^GPSD,(.)=(.*)$/) {
257 $! = &Errno::EBADMSG; 270 $! = &Errno::EBADMSG;
258 $self->event ("error"); 271 $self->event ("error");
259 return $self->retry; 272 return $self->retry;
260 } 273 }
261 274
262 my ($type, $data) = ($1, $2); 275 my ($type, $data) = ($1, $2);
276
277 #warn "$type=$data\n";#d#
263 278
264 $self->{state}{$type} = [$data => $self->{now}]; 279 $self->{state}{$type} = [$data => $self->{now}];
265 280
266 if ($type eq "O") { 281 if ($type eq "O") {
267 my @data = split /\s+/, $data; 282 my @data = split /\s+/, $data;
273 if (@data > 3) { 288 if (@data > 3) {
274 # the gpsd time is virtually useless as it is truncated :/ 289 # 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)) { 290 for (qw(tag _time _terr lat lon alt herr verr bearing speed vspeed berr serr vserr mode)) {
276 $type = shift @data; 291 $type = shift @data;
277 $fix->{$_} = $type eq "?" ? undef : $type; 292 $fix->{$_} = $type eq "?" ? undef : $type;
293 }
294
295 if (my $s = $self->{stretch}) {
296 $s = 1 / $s;
297
298 $fix->{herr} *= $s; # ?
299 $fix->{verr} *= $s; # ?
300 $fix->{berr} *= $s; # ?
301 $fix->{serr} *= $s; # ?
302 $fix->{vserr} *= $s; # ?
303
304 $fix->{speed} *= $s;
305 $fix->{vspeed} *= $s;
278 } 306 }
279 307
280 $fix->{mode} = 2 if $fix->{mode} eq "?"; # arbitrary choice 308 $fix->{mode} = 2 if $fix->{mode} eq "?"; # arbitrary choice
281 } else { 309 } else {
282 $fix->{mode} = 1; 310 $fix->{mode} = 1;
348 # if we likely have zero speed, return the point itself 376 # if we likely have zero speed, return the point itself
349 ($fix->{lat}, $fix->{lon}) 377 ($fix->{lat}, $fix->{lon})
350 } 378 }
351} 379}
352 380
381sub log {
382 my ($self, @arg) = @_;
383
384 syswrite $self->{logfh}, JSON::encode_json ([AnyEvent->time, @arg]) . "\n"
385 if $self->{logfh};
386}
387
388=item $gps->record_log ($path)
389
390If C<$path> is defined, then that file will be created or truncated and a
391log of all (raw) packets received will be written to it. This log file can
392later be replayed by calling C<< $gps->replay_log ($path) >>.
393
394If C<$path> is undefined then the log will be closed.
395
396=cut
397
398sub record_log {
399 my ($self, $path) = @_;
400
401 if (defined $path) {
402 $self->record_log;
403
404 require JSON;
405
406 open $self->{logfh}, ">:perlio", $path
407 or Carp::croak "$path: $!";
408
409 $self->log (start => $VERSION, 0, 0, { interval => $self->{interval} });
410 } elsif ($self->{logfh}) {
411 $self->log ("stop");
412 delete $self->{logfh};
413 }
414}
415
416=item $gps->replay_log ($path, %options)
417
418Replays a log file written using C<record_log> (or stops replaying when
419C<$path> is undefined). While the log file replays, real GPS events will
420be ignored. This comes in handy when testing.
421
422Please note that replaying a log will change configuration options that
423will not be restored, so it's best not to reuse a gpsd object after a
424replay.
425
426The options include:
427
428=over 4
429
430=item compress => 1
431
432If set to a true value (default: false), then passages without fix will be
433replayed much faster than passages with fix. The same happens for passages
434without much movement.
435
436=item stretch => $factor
437
438Multiplies all times by the given factor. Values < 1 make the log replay
439faster, values > 1 slower. Note that the frequency of fixes will not be
440increased, o stretch factors > 1 do not work well.
441
442A stretch factor of zero is not allowed, but if you want to replay a log
443instantly you may speicfy a very low value (e.g. 1e-10).
444
445=back
446
447=cut
448
449sub replay_log {
450 my ($self, $path, %option) = @_;
451
452 if (defined $path) {
453 $self->replay_log;
454
455 require JSON;
456
457 open my $fh, "<:perlio", $path
458 or Carp::croak "$path: $!";
459
460 $self->{stretch} = $option{stretch} || 1;
461 $self->{compress} = $option{compress};
462
463 $self->{imterval} /= $self->{stretch};
464
465 Scalar::Util::weaken $self;
466
467 $self->{replay_cb} = sub {
468 my $line = <$fh>;
469
470 if (2 > length $line) {
471 $self->replay_log;
472 } else {
473 my ($time, $type, @data) = @{ JSON::decode_json ($line) };
474
475 $time *= $self->{stretch};
476
477 if ($type eq "start") {
478 my ($module_version, $major_version, $minor_version, $args) = @data;
479
480 $self->{interval} = ($args->{interval} || 1) / $self->{stretch};
481 }
482
483 if (
484 $type eq "start"
485 or ($self->{compress}
486 and $self->{fix} && ($self->{fix}{mode} < 2 || $self->{fix}{speed} < 5))
487 ) {
488 $self->{replay_now} = $time;
489 }
490
491 $self->{replay_timer} = AnyEvent->timer (after => $time - $self->{replay_now}, cb => sub {
492 $self->{replay_now} = $time;
493 $self->{command} = []; # no can do
494 $self->feed ($data[0]) if $type eq "raw";
495 $self->{replay_cb}();
496 });
497 }
498 };
499
500 $self->{replay_cb}();
501
502 } else {
503 delete $self->{stretch};
504 delete $self->{compress};
505 delete $self->{replay_timer};
506 delete $self->{replay_cb};
507 }
508}
509
353=back 510=back
354 511
355=head1 SEE ALSO 512=head1 SEE ALSO
356 513
357L<AnyEvent>. 514L<AnyEvent>.

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines