GPS 虚拟驱动程序
虚拟串行 (COM) 驱动程序,可将 NMEA 0183 v2.3 的 GPS 输入转换为 v2.0,以便被旧式 GPS 应用程序使用。
引言
本文介绍如何使用虚拟串行 (COM) 端口读取和转换 GPS 数据流,使其格式从一种转换为另一种,从而使旧式 GPS 应用程序能够与新型 GPS 接收器配合使用。
背景
我有一台 HPC CASSIO Cassiopeia A-10,运行 Windows CE 2.00,我专门将其用于 GPS 导航。我使用 TeleType 应用程序 (WinCE 2.00) 和 Delorme Tripmate GPS。最近,我将我的 GPS 升级到 Pharos GPS-500 SiRF III,但 TeleType 应用程序无法与之配合使用。原来,该应用程序使用的是比 GPS-500 更旧版本的 NMEA 协议。该应用程序使用 NMEA 0183 v2.0,而 GPS-500 使用 NMEA 0183 v2.3。
该应用程序实际上只使用了 NMEA 协议中的两个句子 - GGA 和 RMC。
GGA — 全球定位系统固定数据
$GPGGA,161229.487,3723.2475,N,12158.3416,W,1,07,1.0,9.0,M,,,,0000*18
名称 | 示例 | 单位 | 描述 |
消息 ID | $GPGGA | GGA 协议头 | |
UTC 时间 | 161229.487 | hhmmss.sss | |
纬度 | 3723.2475 | ddmm.mmmm | |
N/S 指示符 | N | N=北或 S=南 | |
经度 | 12158.3416 | dddmm.mmmm | |
E/W 指示符 | W | E=东或 W=西 | |
位置固定指示符 | 1 | 参见表 1-4 | |
使用的卫星数 | 7 | 范围 0 至 12 | |
HDOP | 1 | 水平精度稀释度 | |
MSL 高度 | 9 | 米 | |
单位 | M | 米 | |
大地水准面分离度 | 米 | ||
单位 | M | 米 | |
差分校正年龄。 | second | 未使用 DGPS 时字段为空。 | |
差分参考站 ID | 0000 | ||
校验和 | *18 | ||
<CR> <LF> | 消息结束符 |
请注意,NMEA v2.0 和 v2.3 之间的 GGA 没有区别。
RMC — 推荐的最小特定 GNSS 数据
$GPRMC,161229.487,A,3723.2475,N,12158.3416,W,0.13,309.62,120598,,A*10
名称 | 示例 | 单位 | 描述 |
消息 ID | $GPRMC | RMC 协议头 | |
UTC 时间 | 161229.487 | hhmmss.sss | |
状态 | A | A=数据有效或 V=数据无效 | |
纬度 | 3723.2475 | ddmm.mmmm | |
N/S 指示符 | N | N=北或 S=南 | |
经度 | 12158.3416 | dddmm.mmmm | |
E/W 指示符 | W | E=东或 W=西 | |
对地速度 | 0.13 | 节 | |
对地航向 | 309.62 | 度 | True |
日期 | 120598 | ddmmyy | |
磁差 | 度 | E=东或 W=西 | |
模式 | A | A=自主,D=DGPS,E= DR (v2.0 中缺失) | |
校验和 | *10 | ||
<CR> <LF> | 消息结束符 |
最后一个字段“Mode
”在 NMEA v2.0 中不存在。为了使我的应用程序正常工作,它需要将 RMC 从 v2.3 (GPS-500) 翻译为 v2.0 (应用程序)。在翻译过程中,需要移除 RMC 数据中的最后一个字段,使其看起来像这样:
$GPRMC,161229.487,A,3723.2475,N,12158.3416,W,0.13,309.62,120598,*20
Using the Code
要构建虚拟串行 (COM) 端口,我使用了本文中提供的优秀示例。(非常感谢作者。)
虚拟端口充当实际端口和应用程序之间的连接。在读取 GPS 数据并将其传递给应用程序的过程中,虚拟端口将 GPS 数据格式从 NMEA 2.3 翻译为 NMEA 2.0,用于 RMC 语句。
以下代码显示了虚拟端口的“read
”函数:
DWORD COM_Read( DWORD hOpenContext, LPVOID pBuffer, DWORD Count )
{
if (hOpenContext != OPEN_CONTEXT)
return 0;
// read from the actual port
DWORD dwBytes = 0;
ReadFile(hComm, pBuffer, Count, &dwBytes, NULL);
// convert the data before it's returned
Convert( pBuffer, dwBytes );
return dwBytes;
}
“Convert
”函数负责查找和转换 RMC 语句。
char * atoh = "0123456789ABCDEF";
// " 0 1 2 3 4 5 6 7 8 9 : ;
// < = > ? @ A B C D E F"
BYTE htoa1[] = {0x00,0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80,0x90,
0,0,0,0,0,0,0,0xA0,0xB0,0xC0,0xD0,0xE0,0xF0};
BYTE htoa2[] = {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,
0,0,0,0,0,0,0,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F};
void Convert( LPVOID pBuffer, DWORD Count )
{
char* pCh = (char*)pBuffer;
char ch, crc1, crc2; DWORD nCRC; BYTE state = 10;
for (DWORD i=0;i<Count; i++)
{
ch = pCh[i];
if (ch == '$')
state = 11;
else
{
switch ( state )
{
case 11: state = ch == 'G'? 12:10; break;
case 12: state = ch == 'P'? 13:10; break;
case 13: state = ch == 'R'? 14:10; break;
case 14: state = ch == 'M'? 15:10; break;
case 15: state = ch == 'C'? 16:10; break;
case 16: state = ch == ','? 20:10; break;
case 20: if (ch == 'A') state = 21; break;
case 21: state = ch == '*'? 22:20; break;
case 22:
crc1 = ch; state = 23; break;
case 23:
{
crc2 = ch;
// get the crc
nCRC = htoa1[crc1 - '0'] + htoa2[crc2 - '0'];
// remove the chars we are deleting
nCRC -= 'A';
nCRC -= ',';
// ... ,,A*72\n
// ^ current pos
// ^ write over pos
pCh[i-4]='*';
pCh[i-3]=atoh[(nCRC & 0xF0) >> 4];
pCh[i-2]=atoh[ nCRC & 0x0F ];
pCh[i-1]=13;
pCh[i-0]=10;
state = 10;
}
break;
}
}
}
}
该函数遍历输入缓冲区 pBuffer
并查找任何 RMC 语句。对于每个 RMC,它会检查末尾的 string
“A*
”并将其移除。它还会计算并更新末尾的 CRC 校验和。
要安装虚拟端口驱动程序,我们需要将驱动程序 DLL 文件复制到设备上的 \windows 文件夹,并在注册表中添加以下条目:
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\Serial6]
"ManagePort"=dword:00000002
"DeviceType"=dword:00000001
"Index"=dword:00000006
"DevConfig"=hex: 10,00,00,00,05,00,00,00,10,01,00,00,00,4b,
00,00,00,00,08,00,00,00,00,00,00
"Tsp"="Unimodem.dll"
"Order"=dword:00000006
"Prefix"="COM"
"FriendlyName"="GPS Virtual Driver"
"Dll"="GpsVirtualDriver.dll"
"ManagePort
" 是 GPS 设备连接到的端口号。在本例中,GPS 通过 PCMCIA 卡连接,并显示为设备上的 COM2。
"Index
" 是虚拟串行端口 COM 号。在本例中,它是 COM6。
要将应用程序部署到设备,我们需要构建一个 cab 文件。这是一个简单的 INF 文件,用于构建 cab 文件:
; sample.inf
;
;==================================================
[Version]
Signature = "$Windows NT$"
Provider = "Gps"
CESignature = "$Windows CE$"
;
; Supports only WinCE 2.00
;
[CEDevice]
VersionMin=2.00
VersionMax=2.00
[CEStrings]
AppName = "Virtual Driver"
InstallDir = %CE2%
[Strings]
reg_path = Drivers\Builtin\Serial6
;==================================================
[DefaultInstall]
CopyFiles = Dllfiles
Addreg = Regkeys
;==================================================
[SourceDisksNames]
1 =, "Common Files",, .
[SourceDisksFiles]
GpsVirtualDriver.dll = 1
;==================================================
; Ouput directories for files & shortcuts
[DestinationDirs]
Dllfiles = 0, %CE2%
[Dllfiles]
GpsVirtualDriver.dll
[Regkeys]
HKLM,%reg_path%,Dll,0x00000000,GpsVirtualDriver.dll
HKLM,%reg_path%,Prefix,0x00000000,COM
HKLM,%reg_path%,FriendlyName,0x00000000,GPS Virtual Driver
HKLM,%reg_path%,Index,0x00010001,6
HKLM,%reg_path%,Order,0x00010001,2
HKLM,%reg_path%,ManagePort,0x00010001,2
HKLM,%reg_path%,DeviceType,0x00010001,1
HKLM,%reg_path%,Tsp,0x00000000,Unimodem.dll
HKLM,%reg_path%,DevConfig,0x00000001,10,00,00,00,05,00,00,00,10,
01,00,00,00,4b,00,00,00,00,08,00,00,00,00,00,00
安装 cab 文件后,需要进行软重置才能使驱动程序开始工作。
关注点
完成这个小型项目充满了乐趣,我学会了如何为 Windows CE 2.00 构建简单的设备驱动程序。现在,我的 Cassiopeia 运行着 TeleType GPS 应用程序和新的 GPS-500 接收器。GPS-500 非常紧凑,安装在我的 PCMCIA 扩展插槽中,从那里获取电力,无需电池和/或笨重的电缆。它还使我能够继续使用我旧而可靠的 Cassiopeia A-10 进行 GPS 导航。
参考文献
- TeleType GPS FAQ: http://www.teletype.com/pages/support/faq.html
- NMEA 参考手册: http://www.sparkfun.com/datasheets/GPS/NMEA Reference Manual1.pdf
历史
- 2007 年 6 月 12 日 - 首次发布