GPS 和 Perl

自从人类起源以来,人们就需要知道自己的位置。如果你正在拖着一只死羚羊回到部落的其他成员那里,知道自己在哪里以及你要去哪里非常重要。在某个时候,其中一个人可能说过“我真的很希望我的祖先把许多卫星送入轨道来使这件事更容易”。那个人可能应该专注于发明轮子,锻造金属,以及将巧克力与花生酱结合起来,但说实话:人类最好的发明之一来自于那些有时会过于自信的人。这意味着今天,我们有一个这样的卫星系统在轨道上帮助你在周围地区导航。

全球定位系统(GPS)最初是由美国国防部发明,用于将导弹引导到目标。后来它被开放给民用,卫星的特性意味着你可以从世界各地接收到它们的信号。不想受制于一个最终由美国军方控制的系统,欧盟一直在发射伽利略系统,而俄罗斯发射了GLONASS。这些系统同样可在全球范围内使用,而且一些接收器可以接收到多种类型。幸运的是,因为有时单独的GPS并不能提供你需要的确切位置。

GPS需要你至少从4颗卫星那里获得良好的信号来精确确定你的位置。GPS扩展板通常在其PCB走线上集成了一个小的天线,但这不会接收到很多信号,尤其是在室内。Adafruit Industries的Adafruit Ultimate GPS Breakout有一个走线天线,可以帮助你开始。它还配备了板上的u.FL插头,可以使用u.FL到SMA适配器插入大多数外部天线。

大多数GPS模块通过简单的串行连接进行连接。在较老的PC上,你可以通过RS232端口访问它们,但现代的计算机很少配备这种端口。USB FTDI电缆适配器可以解决这个问题。

在Raspberry Pi上,GPIO引脚上有一组可以用于串行设备的引脚。它可以通过/dev/ttyAMA0访问。这里有一个小问题:Pi使用这个串行设备来输出控制台信息。如果你想要在没有网络的情况下运行无头Pi,这很方便,但它会妨碍我们与GPS设备的接口。

为了解决这个问题,需要编辑两个文件。第一个是/etc/inittab,其中包含一个将getty附加到设备的条目

T0:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100

通过在行首放置#来注释掉这一行。

第二个文件是/boot/cmdline.txt,其中包含在启动时传递给Linux内核的参数。这会导致内核启动信息传递到设备。它看起来可能像这样

dwc_otg.lpm_enable=0 console=ttyAMA0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait

删除任何引用ttyAMA0的参数。

现在我们的Raspberry Pi准备好了,连接GPS设备。注意你必须交叉接收和传输线。GPS的Tx线连接到Pi的Rx线,反之亦然。

连接后,你应该可以在终端上运行screen(是的,你可以使用screen来处理串行终端)

screen /dev/ttyAMA0 9600

这应该会向你显示类似以下的数据

$GPGGA,235119.315,,,,,0,00,,,M,,M,,*72
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GPGSV,1,1,03,10,,,21,16,,,30,07,,,26*7F
$GPRMC,235119.315,V,,,,,0.00,0.00,060315,,,N*46
$GPVTG,0.00,T,,M,0.00,N,0.00,K,N*32
$GPGGA,235120.091,,,,,0,00,,,M,,M,,*77
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GPRMC,235120.091,V,,,,,0.00,0.00,060315,,,N*43
$GPVTG,0.00,T,,M,0.00,N,0.00,K,N*32
$GPGGA,235120.310,,,,,0,00,,,M,,M,,*7D
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GPRMC,235120.310,V,,,,,0.00,0.00,060315,,,N*49

这里的数据是国家海事电子协会(NMEA)标准的一部分。大多数GPS接收器以这种格式提供数据。注意所有的逗号吗?这表示接收器还没有获得良好的定位,可能是因为我把它们带进了室内。即使数据很糟糕,接收器也会始终尝试发送数据。

既然是Perl,已经有了一个可以解析这些数据的CPAN模块: GPS::NMEA。下面是它的实际应用

use GPS::NMEA;
my $gps = GPS::NMEA->new(
    Port => '/dev/ttyAMA0',
    Baud => 9600,
);

while(1) {
    my($ns,$lat,$ew,$lon) = $gps->get_position;
    # decimal portion is arcminutes, so convert to degrees
    $lat = int($lat) + ($lat - int($lat)) * 1.66666667;
    $lon = int($lon) + ($lon - int($lon)) * 1.66666667;

    say "($ns,$lat,$ew,$lon)";
}

这将持续打印来自GPS接收器的位置数据

(N,43.052243,W,89.217520)
(N,43.052240,W,89.217519)
(N,43.052237,W,89.217518)

我不知道人们在GPS出现之前是如何做到这一点的。很可能非常糟糕。我很高兴Perl今天能帮助做到这一点。

更新: 添加了弧分转换为度数的功能。感谢Jonathan Coop指出这一点。2015-04-04


本文最初发布在 PerlTricks.com

标签

蒂姆·默里

蒂姆·默里是一位Perl程序员和硬件专家。他是UAV::Pilot的创造者,该库是事实上的Perl无人机库标准。他经常撰写博客,内容关于Perl和硬件破解。

浏览他们的文章

反馈

这篇文章有什么问题吗?请在GitHub上打开一个issue或pull request来帮助我们。