package GPS; use strict; use warnings; use POSIX (); use IO::Socket::INET; our $VERSION = 0.01; sub new { my ($class, %arg) = @_; $arg{port} ||= 2947; $arg{host} ||= 'localhost'; print "$class\n"; my $m = $class =~ /Raw$/ ? 'Coro::Socket' : 'IO::Socket::INET'; my $sock = new $m PeerHost => $arg{host}, PeerPort => $arg{port} or die 'GPSD ($arg{host}:$arg{port}): $!'; print $sock "HELO\n"; <$sock> =~ /GPSD/ or die 'no GPSD'; if($class =~/Raw$/) { print $sock "R\n"; <$sock> =~ /GPSD,R=1/ or die "can't enable raw-mode"; } bless { fh => $sock, minpoll => $arg{minpoll} || 1, last => 0, data => {}, }, $class; } sub poll { my $self = shift; print {$self->{fh}} "dpva\n"; if (readline ($self->{fh}) =~ m%GPSD,D=(\d\d)/(\d\d)/(\d\d\d\d) (\d\d):(\d\d):(\d\d),P=([\-0-9.]+) ([\-0-9.]+),V=([0-9.]+),A=([\-0-9.]+)%) { local $ENV{TZ} = "+0000"; $self->{data} = { time => (POSIX::mktime $6,$5,$4,$2,$1-1,$3-1900,0,0), lat => $7, long => $8, v => $9, alt => $10, }; $self->{last} = time; } } sub data { $_[0]->poll if time - $_[0]{last} >= $_[0]{minpoll}; $_[0]{data}; } package GPS::Raw; our @ISA = qw(GPS); use strict; use warnings; use POSIX qw(modf); use Coro::Socket; use Coro::Event; use Coro; sub PI { 3.14159265 } # not used :) sub latlon($$) { my ($t1,$t2) = modf($_[0] / 100); $t2 = $t2 + $t1*100 / 60; $t2 *= -1 if $_[1] =~ /[SW]/i; $t2 } our %cbs = ( 'GPGLL' => sub { my ($res, @a) = @_; $res->{Plain}{Latitude} = $a[0]; $res->{lat} = latlon $a[0], $a[1]; $res->{Plain}{NS} = $a[1]; $res->{Plain}{Longitude} = $a[2]; $res->{long} = latlon $a[2], $a[3]; $res->{Plain}{WE} = $a[3]; $res->{Plain}{UTC} = $a[4]; }, 'PGRMZ' => sub { my ($res, @a) = @_; $res->{Plain}{Altitude} = $a[0]; $res->{alt} = $a[0] / 3.2808399; # foot -> meters }, 'GPRMC' => sub { my ($res, @a) = @_; $res->{Plain}{Speed} = $a[6]; $res->{v} = $a[6] * 1.852; # knoten :) $res->{Plain}{Course} = $a[7]; $res->{course} = $a[7]; }, 'GPRMB' => sub { my ($res, @a) = @_; $res->{WP}{origin} = $a[2]; $res->{WP}{destination} = $a[3]; $res->{WP}{destination} = $a[4]; $res->{WP}{desLat} = latlon $a[5], $a[6]; $res->{WP}{desLong} = latlon $a[7], $a[8]; $res->{WP}{distance} = $a[9]* 1.852; #seemeilen -> km $res->{WP}{bearing} = $a[10]; $res->{WP}{speed} = $a[11] *1852; #knoten->km/h }, ); sub _chksum($) { my ($line,$cs) = (shift,0); $line =~ m/\$([^\*]+)\*([0-9A-F]{2})$/ or do { warn "illegal line \"$line\""; return 0; }; my ($str, $pcs) = ($1, $2); map {$cs ^= $_} unpack ("C*", $str); return 1 if $cs == hex($pcs); warn "illegal checksum. packet=$pcs computed=$cs"; return 0; } sub get_line($) { my $sock = $_[0]{fh}; cserr: my $l = readline $sock; $l =~ s/[\015\012\0]*//g; _chksum($l) or goto cserr; warn "unknown line \"$l\"" unless $l =~ /^\$([^,]+),([^\*]+)\*[A-F0-9]{2}$/; my ($cmd, $arg) = ($1,$2); ($l, $cmd, $arg); } sub daemon($) { my $self = shift; async { my $self = shift; my $res = $self->{data}; use Data::Dumper; $res->{lines} = 0; while(1) { my($line, $cmd, $arg) = $self->get_line; $res->{lines}++; $res->{Raw}{$cmd} = $arg; &{$cbs{$cmd}}($res, split /,/, $arg) if exists $cbs{$cmd}; } } $self; } sub data { cede; $_[0]{data}; } 1;