AnyEvent-GPSD
view release on metacpan or search on metacpan
Called each time the satellite info changes, also on first connect. Each
C<satellite-info> hash contains at least the following members (mnemonic:
all keys have three letters):
C<prn> holds the satellite PRN (1..32 GPS, anything higher is
wASS/EGNOS/MCAS etc, see L<GPS::PRN>).
C<ele>, C<azi> contain the elevation (0..90) and azimuth (0..359) of the satellite.
C<snr> contains the signal strength in decibals (28+ is usually the
minimum value for a good fix).
C<fix> contains either C<1> to indicate that this satellite was used for
the last position fix, C<0> otherwise. EGNOS/WAAS etc. satellites will
always show as C<0>, even if their correction info was used.
The passed hash references are read-only.
=item on_fix => $cb->({point})
Called regularly (usually about once/second), even when there is no
connection to the GPSD (so is useful to update your idea of the current
position). The passed hash reference must I<not> be modified in any way.
If C<mode> is C<2> or C<3>, then the C<{point}> hash contains at least the
following members, otherwise it is undefined which members exist. Members
whose values are not known are C<undef> (usually the error values, speed
and so on).
time when this fix was received (s)
lat latitude (S -90..90 N)
lon longitude (W -180..180 E)
alt altitude
herr estimated horizontal error (m)
verr estimated vertical error (m)
bearing bearing over ground (0..360)
berr estimated error in bearing (degrees)
speed speed over ground (m/s)
serr estimated error in speed over ground (m/s)
vspeed vertical velocity, positive = upwards (m/s)
vserr estimated error in vspeed (m/s)
mode 1 = no fix, 2 = 2d fix, 3 = 3d fix
=back
=cut
sub new {
my $class = shift;
my $self = bless {
@_,
interval => 1,
fix => { time => AnyEvent->now, mode => 1 },
}, $class;
$self->interval_timer;
$self->connect;
$self
}
sub DESTROY {
my ($self) = @_;
$self->record_log;
}
sub event {
my $event = splice @_, 1, 1, ();
#warn "event<$event,@_>\n";#d#
if ($event = $_[0]{"on_$event"}) {
&$event;
}
}
sub retry {
my ($self) = @_;
delete $self->{fh};
delete $self->{command};
Scalar::Util::weaken $self;
$self->{retry_w} = AnyEvent->timer (after => 1, cb => sub {
delete $self->{retry_w};
$self->connect;
});
}
# make sure we send "no fix" updates when we lose connectivity
sub interval_timer {
my ($self) = @_;
$self->{interval_w} = AnyEvent->timer (after => $self->{interval}, cb => sub {
if (AnyEvent->now - $self->{fix}{time} > $self->{interval} * 1.9) {
$self->{fix}{mode} = 1;
$self->event (fix => $self->{fix});
}
$self->interval_timer;
});
Scalar::Util::weaken $self;
}
sub connect {
my ($self) = @_;
return if $self->{fh};
AnyEvent::Socket::tcp_connect $self->{host} || "localhost", $self->{port} || 2947, sub {
my ($fh) = @_;
return unless $self;
if ($fh) {
# unbelievable, but true: gpsd does not support command pipelining.
# it's an immensely shitty piece of software, actually, as it blocks
# randomly and for extended periods of time, has a surprisingly broken
# and non-configurable baud autoconfiguration system (it does stuff
# like switching to read-only mode when my bluetooth gps mouse temporarily
# loses the connection etc.) and uses rather idiotic and wasteful
# programming methods.
$self->{fh} = new AnyEvent::Handle
fh => $fh,
low_delay => 1,
on_error => sub {
$self->event ("error");
$self->retry;
},
on_eof => sub {
$! = &Errno::EPIPE;
$self->event ("error");
$self->log ("disconnect");
$self->retry;
},
on_read => sub {
$_[0]{rbuf} =~ s/^([^\015\012]*)\015\012//
or return;
$self->feed ($1)
unless $self->{replay_cb};
},
;
$self->send ("w");
$self->send ("o");
$self->send ("y");
$self->send ("c");
$self->event ("connect");
$self->log ("connect");
} else {
$self->event ("error");
}
};
Scalar::Util::weaken $self;
}
If set to a true value (default: false), then passages without fix will be
replayed much faster than passages with fix. The same happens for passages
without much movement.
=item stretch => $factor
Multiplies all times by the given factor. Values < 1 make the log replay
faster, values > 1 slower. Note that the frequency of fixes will not be
increased, o stretch factors > 1 do not work well.
A stretch factor of zero is not allowed, but if you want to replay a log
instantly you may speicfy a very low value (e.g. 1e-10).
=back
=cut
sub replay_log {
my ($self, $path, %option) = @_;
if (defined $path) {
$self->replay_log;
require JSON;
open my $fh, "<:perlio", $path
or Carp::croak "$path: $!";
$self->{stretch} = $option{stretch} || 1;
$self->{compress} = $option{compress};
$self->{imterval} /= $self->{stretch};
Scalar::Util::weaken $self;
$self->{replay_cb} = sub {
my $line = <$fh>;
if (2 > length $line) {
$self->replay_log;
} else {
my ($time, $type, @data) = @{ JSON::decode_json ($line) };
$time *= $self->{stretch};
if ($type eq "start") {
my ($module_version, $major_version, $minor_version, $args) = @data;
$self->{interval} = ($args->{interval} || 1) / $self->{stretch};
}
if (
$type eq "start"
or ($self->{compress}
and $self->{fix} && ($self->{fix}{mode} < 2 || $self->{fix}{speed} < $self->{min_speed}))
) {
$self->{replay_now} = $time;
}
$self->{replay_timer} = AnyEvent->timer (after => $time - $self->{replay_now}, cb => sub {
$self->{replay_now} = $time;
$self->{command} = []; # no can do
$self->feed ($data[0]) if $type eq "raw";
$self->{replay_cb}();
});
}
};
$self->{replay_cb}();
} else {
delete $self->{stretch};
delete $self->{compress};
delete $self->{replay_timer};
delete $self->{replay_cb};
}
}
=back
=head1 SEE ALSO
L<AnyEvent>.
=head1 AUTHOR
Marc Lehmann <schmorp@schmorp.de>
http://home.schmorp.de/
=cut
1
( run in 1.643 second using v1.01-cache-2.11-cpan-39bf76dae61 )