衛星導航技術已廣泛應用於民用與軍事領域。本文以美國 GPS 系統為例,詳細說明如何在 Windows 平台上透過 RS-232 序列埠接收 GPS 接收機輸出的 NMEA 0183 格式資料,並解析出經緯度、速度、時間等關鍵定位參數。
// 以非同步方式開啟 COM1
m_hCom = CreateFile(_T("COM1"), GENERIC_READ|GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
SetCommMask(m_hCom, EV_RXCHAR); // 設定接收字元事件
SetupComm(m_hCom, READBUFLEN, WRITEBUFLEN); // 設定緩衝區大小
PurgeComm(m_hCom, PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);
// 設定逾時參數
CommTimeOuts.ReadIntervalTimeout = MAXDWORD;
CommTimeOuts.ReadTotalTimeoutMultiplier = 0;
CommTimeOuts.ReadTotalTimeoutConstant = 0;
SetCommTimeouts(m_hCom, &CommTimeOuts);
// 設定 DCB 參數:4800 bps, 8 data, odd parity, 1 stop bit
GetCommState(m_hCom, &dcb);
dcb.BaudRate = CBR_4800; dcb.ByteSize = 8; dcb.Parity = ODDPARITY; dcb.StopBits = ONESTOPBIT;
SetCommState(m_hCom, &dcb);
while (true)
{
WaitCommEvent(m_hCom, &dwEvtMask, NULL);
if ((dwEvtMask & EV_RXCHAR) == EV_RXCHAR)
{
if (ComStat.cbInQue > 0)
ReadFile(m_hCom, m_readbuf, ComStat.cbInQue, &nLength, &olRead);
}
}
GPS 接收機持續輸出多種句型,$GPRMC 包含最常用的導航資訊:
$GPRMC,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,<10>,<11>*hh <1> UTC 時間 hhmmss <2> 狀態 A=有效 V=無效 <3> 緯度 ddmm.mmmm <4> N/S 半球 <5> 經度 dddmm.mmmm <6> E/W 半球 <7> 地面速度 0.0~999.9 節 <8> 方位角 000.0~359.9 度 <9> 日期 ddmmyy <10> 磁變角 000.0~180.0 度 <11> 磁變方向 E/W
for (int i=0; i<bufLen; ++i)
{
if (Data[i] == '$') SectionID = 0; // 偵測句首
else if (Data[i] == 10) { /* LF 句尾 */ }
else if (Data[i] == ',') SectionID++; // 逗號計數
else
{
switch (SectionID)
{
case 1: m_sTime += Data[i]; break; // UTC 時間
case 2: if (Data[i]=='A') valid=true; break; // 定位有效性
case 3: m_sPositionY += Data[i]; break; // 緯度
case 5: m_sPositionX += Data[i]; break; // 經度
case 9: m_sDate += Data[i]; break; // 日期
}
}
}
// 時間轉北京時間 (UTC+8)
str.Format(_T("%c%c"), buf[0], buf[1]);
hour = (atoi(str) + 8) % 24;
// 緯度 ddmm.mmmm -> 度分格式
str.Format(_T("%c%c"), buf[0], buf[1]); deg = atoi(str);
str.Format(_T("%c%c%c%c%c%c%c"), buf[2],buf[3],buf[4],buf[5],buf[6],buf[7],buf[8]);
lat = deg * 60 + atof(str); // 單位:分
// 經度 dddmm.mmmm (可能 2 或 3 位數度)
if (len==10) { str.Format(_T("%c%c%c"), buf[0],buf[1],buf[2]); deg=atoi(str); ... }
else { str.Format(_T("%c%c"), buf[0],buf[1]); deg=atoi(str); ... }
本文結合完整程式碼示範了從序列埠開啟、參數設定、非同步接收、NMEA 句型解析到座標數值轉換的完整流程。開發者可據此整合至 GIS、RS 等上層應用。測試環境:Windows 98 + Visual C++ 6.0。