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.1 by root, Wed Jul 2 04:57:02 2008 UTC vs.
Revision 1.6 by root, Fri Jul 25 13:26:06 2008 UTC

88 88
89C<snr> contains the signal strength in decibals (28+ is usually the 89C<snr> contains the signal strength in decibals (28+ is usually the
90minimum value for a good fix). 90minimum value for a good fix).
91 91
92C<fix> contains either C<1> to indicate that this satellite was used for 92C<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 93the last position fix, C<0> otherwise. EGNOS/WAAS etc. satellites will
94always show as C<0>, even if their correction info was used. 94always show as C<0>, even if their correction info was used.
95 95
96The passed hash references are read-only.
97
96=item on_fix => $cb->({point}) 98=item on_fix => $cb->({point})
97 99
98Called regularly. The C<{point}> hash contains at least the following 100Called regularly (usually about once/second), even when there is no
99members: 101connection to the GPSD (so is useful to update your idea of the current
102position). The passed hash reference must I<not> be modified in any way.
103
104If C<mode> is C<2> or C<3>, then the C<{point}> hash contains at least the
105following members, otherwise it is undefined which members exist. Members
106whose values are not known are C<undef> (usually the error values, speed
107and so on).
100 108
101 time when this fix was received (s) 109 time when this fix was received (s)
102 110
103 lat latitude (S -90..90 N) 111 lat latitude (S -90..90 N)
104 lon longitude (W -180..180 E) 112 lon longitude (W -180..180 E)
122 130
123sub new { 131sub new {
124 my $class = shift; 132 my $class = shift;
125 my $self = bless { 133 my $self = bless {
126 @_, 134 @_,
135 interval => 1,
136 fix => { time => AnyEvent->now, mode => 1 },
127 }, $class; 137 }, $class;
128 138
139 $self->interval_timer;
129 $self->connect; 140 $self->connect;
130 141
131 $self 142 $self
143}
144
145sub DESTROY {
146 my ($self) = @_;
147
148 $self->record_log;
132} 149}
133 150
134sub event { 151sub event {
135 my $event = splice @_, 1, 1, (); 152 my $event = splice @_, 1, 1, ();
136 153
137 warn "event<$event,@_>\n";#d# 154 #warn "event<$event,@_>\n";#d#
138 if ($event = $_[0]{"on_$event"}) { 155 if ($event = $_[0]{"on_$event"}) {
139 &$event; 156 &$event;
140 } 157 }
141} 158}
142 159
143sub retry { 160sub retry {
144 my ($self) = @_; 161 my ($self) = @_;
145 162
146 delete $self->{fh}; 163 delete $self->{fh};
164 delete $self->{command};
147 165
148 Scalar::Util::weaken $self; 166 Scalar::Util::weaken $self;
149 $self->{retry_w} = AnyEvent->timer (after => 1, cb => sub { 167 $self->{retry_w} = AnyEvent->timer (after => 1, cb => sub {
150 delete $self->{retry_w}; 168 delete $self->{retry_w};
151 $self->connect; 169 $self->connect;
152 }); 170 });
171}
172
173# make sure we send "no fix" updates when we lose connectivity
174sub interval_timer {
175 my ($self) = @_;
176
177 $self->{interval_w} = AnyEvent->timer (after => $self->{interval}, cb => sub {
178 if (AnyEvent->now - $self->{fix}{time} > $self->{interval} * 1.9) {
179 $self->{fix}{mode} = 1;
180 $self->event (fix => $self->{fix});
181 }
182
183 $self->interval_timer;
184 });
185
186 Scalar::Util::weaken $self;
153} 187}
154 188
155sub connect { 189sub connect {
156 my ($self) = @_; 190 my ($self) = @_;
157 191
177 on_error => sub { 211 on_error => sub {
178 $self->event ("error"); 212 $self->event ("error");
179 $self->retry; 213 $self->retry;
180 }, 214 },
181 on_eof => sub { 215 on_eof => sub {
182 $! = &Errno::EBADMSG; 216 $! = &Errno::EPIPE;
183 $self->event ("error"); 217 $self->event ("error");
218 $self->log ("disconnect");
184 $self->retry; 219 $self->retry;
185 }, 220 },
186 on_read => sub { 221 on_read => sub {
187 $_[0]{rbuf} =~ s/^([^\015\012]*)\015\012// 222 $_[0]{rbuf} =~ s/^([^\015\012]*)\015\012//
188 or return; 223 or return;
189 224
190 $self->feed ($1); 225 $self->feed ($1)
226 unless $self->{replay_cb};
191 }, 227 },
192 ; 228 ;
193 229
194 $self->send ("w"); 230 $self->send ("w");
195 $self->send ("o"); 231 $self->send ("o");
196 $self->send ("y"); 232 $self->send ("y");
233 $self->send ("c");
197 234
198 $self->event ("connect"); 235 $self->event ("connect");
236 $self->log ("connect");
199 } else { 237 } else {
200 $self->event ("error"); 238 $self->event ("error");
201 } 239 }
202 }; 240 };
203 241
223sub feed { 261sub feed {
224 my ($self, $line) = @_; 262 my ($self, $line) = @_;
225 263
226 $self->{now} = AnyEvent->now; 264 $self->{now} = AnyEvent->now;
227 265
266 $self->log (raw => $line)
267 if $self->{logfh};
268
228 unless ($line =~ /^GPSD,(.)=(.*)$/) { 269 unless ($line =~ /^GPSD,(.)=(.*)$/) {
229 $! = &Errno::EBADMSG; 270 $! = &Errno::EBADMSG;
230 $self->event ("error"); 271 $self->event ("error");
231 return $self->retry; 272 return $self->retry;
232 } 273 }
233 274
234 my ($type, $data) = ($1, $2); 275 my ($type, $data) = ($1, $2);
235 276
277 #warn "$type=$data\n";#d#
278
236 $self->{state}{$type} = [$data => $self->{now}]; 279 $self->{state}{$type} = [$data => $self->{now}];
237 280
238 if ($type eq "O") { 281 if ($type eq "O") {
239 my @data = split /\s+/, $data; 282 my @data = split /\s+/, $data;
283
284 my $fix = $self->{fix};
285
240 my %fix = (time => $self->{now}); 286 $fix->{time} = $self->{now};
241 287
242 if (@data > 3) { 288 if (@data > 3) {
243 # the gpsd time is virtually useless as it is truncated :/ 289 # the gpsd time is virtually useless as it is truncated :/
244 $fix{$_} = shift @data 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)) {
291 $type = shift @data;
292 $fix->{$_} = $type eq "?" ? undef : $type;
293 }
245 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;
306 }
307
246 $fix{mode} = 2 if $fix{mode} eq "?"; # arbitrary choice 308 $fix->{mode} = 2 if $fix->{mode} eq "?"; # arbitrary choice
247 } else { 309 } else {
248 $fix{mode} = 1; 310 $fix->{mode} = 1;
249 } 311 }
250 312
251 $self->{fix} = \%fix;
252 $self->event (fix => \%fix); 313 $self->event (fix => $fix);
253 314
254 } elsif ($type eq "Y") { 315 } elsif ($type eq "Y") {
255 my (undef, @sats) = split /:/, $data; 316 my (undef, @sats) = split /:/, $data;
256 317
257 $self->{satellite_info} = [map { 318 $self->{satellite_info} = [map {
264 fix => $sat[4], 325 fix => $sat[4],
265 } 326 }
266 } @sats]; 327 } @sats];
267 328
268 $self->event (satellite_update => $self->{satellite_info}); 329 $self->event (satellite_update => $self->{satellite_info});
330
331 } elsif ($type eq "C") {
332 $self->{interval} = $data >= 1 ? $data * 1 : 1;
269 } 333 }
270 334
271 # we (wrongly) assume that gpsd responses are always in response 335 # we (wrongly) assume that gpsd responses are always in response
272 # to an earlier command 336 # to an earlier command
273 337
282This returns an estimate of the current position based on the last fix and 346This returns an estimate of the current position based on the last fix and
283the time passed since then. Useful for interactive applications where you 347the time passed since then. Useful for interactive applications where you
284want more frequent updates, but not very useful to store, as the next fix 348want more frequent updates, but not very useful to store, as the next fix
285might well be totally off. 349might well be totally off.
286 350
287If the fix is older then C<$max_seconds> (default: C<1.9>) or if no fix is 351If the fix is older then C<$max_seconds> (default: C<1.9> times the update
288available, returns the empty list. 352interval, i.e. usually C<1.9> seconds) or if no fix is available, returns
353the empty list.
289 354
290=cut 355=cut
291 356
292sub estimate { 357sub estimate {
293 my ($self, $max) = @_; 358 my ($self, $max) = @_;
294 359
295 $max ||= 1.9 unless defined $max; 360 $max ||= 1.9 * $self->{interval} unless defined $max;
296 361
297 my $geo = $self->{geo_forward} ||= new Geo::Forward; 362 my $geo = $self->{geo_forward} ||= new Geo::Forward;
298 363
299 my $fix = $self->{fix} or return; 364 my $fix = $self->{fix} or return;
300 $fix->{mode} >= 2 or return; 365 $fix->{mode} >= 2 or return;
311 # if we likely have zero speed, return the point itself 376 # if we likely have zero speed, return the point itself
312 ($fix->{lat}, $fix->{lon}) 377 ($fix->{lat}, $fix->{lon})
313 } 378 }
314} 379}
315 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
316=back 510=back
317 511
318=head1 SEE ALSO 512=head1 SEE ALSO
319 513
320L<AnyEvent>. 514L<AnyEvent>.

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines