NMEA封裝格式
是由美國國家海洋電子協會(National Marine Electronics Association,NMEA)所制定的GPS協定標準規格,其制定了GPS 上的所有資料格式與資料傳輸的通訊協定,其中還訂下了所有航海電子儀器用的通訊標準,而這些介面協定採用ASCII碼輸出方式,協議定義了若干代表不同含義的語句,語句格式如下所示。
符號(ASCII)
|
定義
|
HEX
|
DEX
|
說明
|
$
|
起始位
|
24
|
36
|
語句起始位
|
aaccc
|
地址域
|
前兩位為位識別符,後三位為語句名
| ||
“,”
|
域分隔符號
|
2C
|
44
|
域分隔符號
|
ddd……ddd
|
資料塊
|
發送的資料內容
| ||
“*”
|
校驗和符
|
2A
|
42
|
星號分隔符號,表明後面的兩位數是校驗和
|
Hh
|
校驗和
|
校驗和
| ||
/
|
終止符
|
0D,0A
|
13,10
|
回車,換行
|
NMEA-0183介面協定定義的主要語句有:GGA、GLL、GSA、GSV、MSS、RMC、VTG、ZDA等。表13-2介紹這些語句所包含的具體內容。
表13-2 常見NMEA-0183語句內容[38]
語句
|
語句內容
|
GGA
|
UTC時間、緯度值、經度值、定位狀態(無效、單點定位、差分)、觀測的GPS衛星個數、HDOP值、GPS橢球高、天線架設高度、差分數據齡期、差分基準站編號、校驗和
|
GLL
|
UTC時間、緯度值、經度值、定位狀態(無效、單點定位、差分)、校驗和
|
GSA
|
定位模式(M-手動,強制二維或三維定位;A-自動,自動二維或三維定位)、定位中使用的衛星ID號、PDOP值、HDOP值、VDOP值
|
GSV
|
視野中的GPS衛星顆數、PRN編號、衛星仰角、距正北的角度(方位角)、信噪比
|
MSS
|
信標台的信號強度、信噪比、信標頻率、串列傳輸速率、通道號
|
RMC
|
UTC時間、定位狀態(A-可用,V-可能有錯誤)、緯度值、經度值、對地速度、日期等
|
VTG
|
對地速度等
|
ZDA
|
UTC時間、年、月、日、當地時區、時區的分鐘值等
|
1.GPS(Global Positioning System):全球衛星定位系統
美國的24顆衛星不停地給地面發GPS信號,只要有一台GPS接收設備,就能定位出你所在的位置高度和速度了,它與電子地圖的結合就是通常所說的GPS功能
美國的24顆衛星不停地給地面發GPS信號,只要有一台GPS接收設備,就能定位出你所在的位置高度和速度了,它與電子地圖的結合就是通常所說的GPS功能
三、gps資料格式
1.從串列埠中讀出的是文本資料,每次讀出一行,一般情況下,一秒種能收到多行資料
2.有效資料以$GP開頭,分為$GPGGA、$GPGSA、$GPGSV、$GPRMC…
nmea資料如下:
$GPGGA,121252.000,3937.3032,N,11611.6046,E,1,05,2.0,45.9,M,-5.7,M,,0000*77
$GPRMC,121252.000,A,3958.3032,N,11629.6046,E,15.15,359.95,070306,,,A*54
$GPVTG,359.95,T,,M,15.15,N,28.0,K,A*04
$GPGGA,121253.000,3937.3090,N,11611.6057,E,1,06,1.2,44.6,M,-5.7,M,,0000*72
$GPGSA,A,3,14,15,05,22,18,26,,,,,,,2.1,1.2,1.7*3D
$GPGSV,3,3,10,29,07,074,,30,07,163,28*7D
$GPGGA,121252.000,3937.3032,N,11611.6046,E,1,05,2.0,45.9,M,-5.7,M,,0000*77
$GPRMC,121252.000,A,3958.3032,N,11629.6046,E,15.15,359.95,070306,,,A*54
$GPVTG,359.95,T,,M,15.15,N,28.0,K,A*04
$GPGGA,121253.000,3937.3090,N,11611.6057,E,1,06,1.2,44.6,M,-5.7,M,,0000*72
$GPGSA,A,3,14,15,05,22,18,26,,,,,,,2.1,1.2,1.7*3D
$GPGSV,3,3,10,29,07,074,,30,07,163,28*7D
注:NMEA0183格式以“$”開始,主要語句有GPGGA,GPRMC,GPGSA,GPGSV,GPVTG,GPZDA等
2.1、 GPS DOP and Active Satellites(GSA)當前衛星資訊
$GPGSA,<1>,<2>,<3>,<4>,,,,,<12>,<13>,<14>, <15>,<16>,<17>,<18>
<1>模式 :M = 手動, A = 自動。
<2>定位型式 1 = 未定位, 2 = 二維定位, 3 = 三維定位。
<3>到<14>PRN 數字:01 至 32 表天空使用中的衛星編號,最多可接收12顆衛星資訊
<2>定位型式 1 = 未定位, 2 = 二維定位, 3 = 三維定位。
<3>到<14>PRN 數字:01 至 32 表天空使用中的衛星編號,最多可接收12顆衛星資訊
(上面藍色處,總共有12個)。
<15> PDOP位置精度因數(0.5~99.9)
<16> HDOP水準精度因數(0.5~99.9)
<17> VDOP垂直精度因數(0.5~99.9)
<18> Checksum.(檢查位).
<15> PDOP位置精度因數(0.5~99.9)
<16> HDOP水準精度因數(0.5~99.9)
<17> VDOP垂直精度因數(0.5~99.9)
<18> Checksum.(檢查位).
2.2、 GPS Satellites in View(GSV)可見衛星資訊
$GPGSV, <1>,<2>,<3>,<4>,<5>,<6>,<7>,?<4>,<5>,<6>,<7>,<8>
<1> GSV語句的總數
<2> 本句GSV的編號
<3> 可見衛星的總數,00 至 12。
<4> 衛星編號, 01 至 32。
<5>衛星仰角, 00 至 90 度。
<6>衛星方位角, 000 至 359 度。實際值。
<7>訊號雜訊比(C/No), 00 至 99 dB;無表未接收到訊號。
<8>Checksum.(檢查位).
<2> 本句GSV的編號
<3> 可見衛星的總數,00 至 12。
<4> 衛星編號, 01 至 32。
<5>衛星仰角, 00 至 90 度。
<6>衛星方位角, 000 至 359 度。實際值。
<7>訊號雜訊比(C/No), 00 至 99 dB;無表未接收到訊號。
<8>Checksum.(檢查位).
第<4>,<5>,<6>,<7>項個別衛星會重複出現,每行最多有四顆衛星。其餘衛星資訊會于次一行出現,若未使用,這些欄位會空白。
2.3、Global Positioning System Fix Data(GGA)GPS定位資訊
$GPGGA,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,M,<10>,M,<11>,<12>*hh
<1> UTC時間,hhmmss(時分秒)格式
<2> 緯度ddmm.mmmm(度分)格式(前面的0也將被傳輸)
<3> 緯度半球N(北半球)或S(南半球)
<4> 經度dddmm.mmmm(度分)格式(前面的0也將被傳輸)
<5> 經度半球E(東經)或W(西經)
<6> GPS狀態:0=未定位,1=非差分定位,2=差分定位,6=正在估算
<7> 正在使用解算位置的衛星數量(00~12)(前面的0也將被傳輸)
<8> HDOP水準精度因數(0.5~99.9)
<9> 海拔高度(-9999.9~99999.9)
<10> 地球橢球面相對大地水準面的高度
<11> 差分時間(從最近一次接收到差分信號開始的秒數,如果不是差分定位將為空)
<12> 差分站ID號0000~1023(前面的0也將被傳輸,如果不是差分定位將為空)
<2> 緯度ddmm.mmmm(度分)格式(前面的0也將被傳輸)
<3> 緯度半球N(北半球)或S(南半球)
<4> 經度dddmm.mmmm(度分)格式(前面的0也將被傳輸)
<5> 經度半球E(東經)或W(西經)
<6> GPS狀態:0=未定位,1=非差分定位,2=差分定位,6=正在估算
<7> 正在使用解算位置的衛星數量(00~12)(前面的0也將被傳輸)
<8> HDOP水準精度因數(0.5~99.9)
<9> 海拔高度(-9999.9~99999.9)
<10> 地球橢球面相對大地水準面的高度
<11> 差分時間(從最近一次接收到差分信號開始的秒數,如果不是差分定位將為空)
<12> 差分站ID號0000~1023(前面的0也將被傳輸,如果不是差分定位將為空)
2.4、Recommended Minimum Specific GPS/TRANSIT Data(RMC)推薦定位資訊
$GPRMC,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,<10>,<11>,<12>*hh
<1> UTC時間,hhmmss(時分秒)格式
<2> 定位狀態,A=有效定位,V=無效定位
<3> 緯度ddmm.mmmm(度分)格式(前面的0也將被傳輸)
<4> 緯度半球N(北半球)或S(南半球)
<5> 經度dddmm.mmmm(度分)格式(前面的0也將被傳輸)
<6> 經度半球E(東經)或W(西經)
<7> 地面速率(000.0~999.9節,前面的0也將被傳輸)
<8> 地面航向(000.0~359.9度,以真北為參考基準,前面的0也將被傳輸)
<9> UTC日期,ddmmyy(日月年)格式
<10> 磁偏角(000.0~180.0度,前面的0也將被傳輸)
<11> 磁偏角方向,E(東)或W(西)
<12> 模式指示(僅NMEA0183 3.00版本輸出,A=自主定位,D=差分,E=估算,N=資料無效)
<2> 定位狀態,A=有效定位,V=無效定位
<3> 緯度ddmm.mmmm(度分)格式(前面的0也將被傳輸)
<4> 緯度半球N(北半球)或S(南半球)
<5> 經度dddmm.mmmm(度分)格式(前面的0也將被傳輸)
<6> 經度半球E(東經)或W(西經)
<7> 地面速率(000.0~999.9節,前面的0也將被傳輸)
<8> 地面航向(000.0~359.9度,以真北為參考基準,前面的0也將被傳輸)
<9> UTC日期,ddmmyy(日月年)格式
<10> 磁偏角(000.0~180.0度,前面的0也將被傳輸)
<11> 磁偏角方向,E(東)或W(西)
<12> 模式指示(僅NMEA0183 3.00版本輸出,A=自主定位,D=差分,E=估算,N=資料無效)
2.5、 Track Made Good and Ground Speed(VTG)地面速度資訊
$GPVTG,<1>,T,<2>,M,<3>,N,<4>,K,<5>*hh
<1> 以真北為參考基準的地面航向(000~359度,前面的0也將被傳輸)
<2> 以磁北為參考基準的地面航向(000~359度,前面的0也將被傳輸)
<3> 地面速率(000.0~999.9節,前面的0也將被傳輸)
<4> 地面速率(0000.0~1851.8公里/小時,前面的0也將被傳輸)
<5> 模式指示(僅NMEA0183 3.00版本輸出,A=自主定位,D=差分,E=估算,N=資料無效)
$GPVTG,<1>,T,<2>,M,<3>,N,<4>,K,<5>*hh
<1> 以真北為參考基準的地面航向(000~359度,前面的0也將被傳輸)
<2> 以磁北為參考基準的地面航向(000~359度,前面的0也將被傳輸)
<3> 地面速率(000.0~999.9節,前面的0也將被傳輸)
<4> 地面速率(0000.0~1851.8公里/小時,前面的0也將被傳輸)
<5> 模式指示(僅NMEA0183 3.00版本輸出,A=自主定位,D=差分,E=估算,N=資料無效)
2.6、GPZDA日期和時間
$GPZDA,<1>,<2>, <3> , <4> , <5> , <6> *CC
<1> UTC時間,hhmmss(時分秒)格式
<2> 日
<3> 月
<4> 年
<5> 本地時區小時便宜量
<6>本地時區分鐘便宜量
<2> 日
<3> 月
<4> 年
<5> 本地時區小時便宜量
<6>本地時區分鐘便宜量
3.0 程式設計
利用strstr函數和sscanf函數解析GPS資料。
GPS輸出的資料格式如下:
$GPGGA,121252.000,3937.3032,N,11611.6046,E,1,05,2.0,45.9,M,-5.7,M,,0000*77
$GPRMC,121252.000,A,3958.3032,N,11629.6046,E,15.15,359.95,070306,,,A*54
$GPVTG,359.95,T,,M,15.15,N,28.0,K,A*04
$GPGGA,121253.000,3937.3090,N,11611.6057,E,1,06,1.2,44.6,M,-5.7,M,,0000*72
$GPGSA,A,3,14,15,05,22,18,26,,,,,,,2.1,1.2,1.7*3D
$GPGSV,3,1,10,18,84,067,23,09,67,067,27,22,49,312,28,15,47,231,30*70
$GPGSV,3,2,10,21,32,199,23,14,25,272,24,05,21,140,32,26,14,070,20*7E
$GPGSV,3,3,10,29,07,074,,30,07,163,28*7D
$GPGGA,121252.000,3937.3032,N,11611.6046,E,1,05,2.0,45.9,M,-5.7,M,,0000*77
$GPRMC,121252.000,A,3958.3032,N,11629.6046,E,15.15,359.95,070306,,,A*54
$GPVTG,359.95,T,,M,15.15,N,28.0,K,A*04
$GPGGA,121253.000,3937.3090,N,11611.6057,E,1,06,1.2,44.6,M,-5.7,M,,0000*72
$GPGSA,A,3,14,15,05,22,18,26,,,,,,,2.1,1.2,1.7*3D
$GPGSV,3,1,10,18,84,067,23,09,67,067,27,22,49,312,28,15,47,231,30*70
$GPGSV,3,2,10,21,32,199,23,14,25,272,24,05,21,140,32,26,14,070,20*7E
$GPGSV,3,3,10,29,07,074,,30,07,163,28*7D
可以看到,GPS模組發送過來的原始資料有很多,但是通常我們只需要其中的一部分資訊就夠用了,比如對於導航的功能,我們只需要以$GPRMC開頭,以換行符結束的一行資訊就夠了。即:
$GPRMC,121252.000,A,3958.3032,N,11629.6046,E,15.15,359.95,070306,,,A*54
$GPRMC,121252.000,A,3958.3032,N,11629.6046,E,15.15,359.95,070306,,,A*54
因此我們需要做的就是從讀取的資料中截取以$GPRMC開頭的一行資訊,然後從中解析出經緯度、日期時間等有效資訊即可。
假設從串口讀取的資料存放在一個字串指標char *raw_buf指向的記憶體單元裏,首先我們通過ANSI C提供的strstr()函數找到以$GPRMC開頭以換行符’\n’結束的字串:
/* find "$GPRMC" from raw_buf */
if ((wellhandled_string = strstr(raw_buf, “$GPRMC”)) != NULL)
{
for (i=0; i {
if (wellhandled_string[i] == '\n')
{
wellhandled_string[i] = '\0'; //replace ‘\n’ with null
}
}
}
strstr() 函數的原型是這樣聲明的:
char *strstr(const char *haystack, const char *needle);
/* find "$GPRMC" from raw_buf */
if ((wellhandled_string = strstr(raw_buf, “$GPRMC”)) != NULL)
{
for (i=0; i {
if (wellhandled_string[i] == '\n')
{
wellhandled_string[i] = '\0'; //replace ‘\n’ with null
}
}
}
strstr() 函數的原型是這樣聲明的:
char *strstr(const char *haystack, const char *needle);
strstr()函數可以在字串haystack中搜索字串needle第一次出現的 位置,並且返回指向字串needle首位址的指標,如果沒有搜索到則返回NULL。因此上面的代碼為我們在讀取的原始資料raw_buf裏搜 索$GPRMC第一次出現的位置,並將返回的指針賦給wellhandled_string,這樣如果搜索成功,則wellhandled_string 就會指向以$GPRMC開始的字串,接下來通過一個for迴圈找到換行符’\n’,將其替換為’\0’,即字串結束符。這樣就得到了一個指向有效資料 的字串指標wellhandled_string。
然後要做的工作就是從wellhandled_string中提取出經緯度、日期時間等資訊。這個工作就可以交給強大的sscanf函數來實現。sscanf函數的原型如下:
int sscanf(const char *str, const char *format, ...);
int sscanf(const char *str, const char *format, ...);
我們都比較熟悉scanf這個函數,scanf可以從標準輸入流讀取與指定格式相符的數 據。sscanf則是從const char *str中讀取。它的強大之處在於可以方便地從字串中取出整數、浮點數和字串等各種類型的資料,而且它還具有類似於正則運算式的匹配功 能,sscanf默認是以空格分隔字串的,如果不是以空格來分割的話,就可以使用%[ ]來指定分割的條件。如%[a-z]表示讀取a到z的所有字元,%[^a-z]表示過濾a-z之間的所有字元,即只要遇到a到z之間的任意字元,轉換立刻 停止。比如:
sscanf(“abcdefABCDEF”, “%[^A-Z]”, str);
printf(“%s\n”, str);
result is: abcdef
%[^A-Z]這樣的匹配格式為我們取遇到大寫字母為止的字串。利用這種匹配方式,我們就可以靈活的操作字串,得到我們想要的結果。
sscanf(“abcdefABCDEF”, “%[^A-Z]”, str);
printf(“%s\n”, str);
result is: abcdef
%[^A-Z]這樣的匹配格式為我們取遇到大寫字母為止的字串。利用這種匹配方式,我們就可以靈活的操作字串,得到我們想要的結果。
現在我們需要從下面的字串中提取有效資訊:
$GPRMC,121252.000,A,3958.3032,N,11629.6046,E,15.15,359.95,070306,,,A*54
$GPRMC,121252.000,A,3958.3032,N,11629.6046,E,15.15,359.95,070306,,,A*54
GPRMC每個欄位的含義如下:
$GPRMC,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,<10>,<11>,<12>*hh
<1> UTC 時間,hhmmss(時分秒)格式
<2> 定位狀態,A=有效定位,V=無效定位
<3> 緯度ddmm.mmmm(度分)格式(前面的0也將被傳輸)
<4> 緯度半球N(北半球)或S(南半球)
<5> 經度dddmm.mmmm(度分)格式(前面的0也將被傳輸)
<6> 經度半球E(東經)或W(西經)
<7> 地面速率(000.0~999.9節,前面的0也將被傳輸)
<8> 地面航向(000.0~359.9度,以真北為參考基準,前面的0也將被傳輸)
<9> UTC日期,ddmmyy(日月年)格式
<10> 磁偏角(000.0~180.0度,前面的0也將被傳輸)
<11> 磁偏角方向,E(東)或W(西)
<12> 模式指示(僅NMEA0183 3.00版本輸出,A=自主定位,D=差分,E=估算,N=資料無效)
$GPRMC,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,<10>,<11>,<12>*hh
<1> UTC 時間,hhmmss(時分秒)格式
<2> 定位狀態,A=有效定位,V=無效定位
<3> 緯度ddmm.mmmm(度分)格式(前面的0也將被傳輸)
<4> 緯度半球N(北半球)或S(南半球)
<5> 經度dddmm.mmmm(度分)格式(前面的0也將被傳輸)
<6> 經度半球E(東經)或W(西經)
<7> 地面速率(000.0~999.9節,前面的0也將被傳輸)
<8> 地面航向(000.0~359.9度,以真北為參考基準,前面的0也將被傳輸)
<9> UTC日期,ddmmyy(日月年)格式
<10> 磁偏角(000.0~180.0度,前面的0也將被傳輸)
<11> 磁偏角方向,E(東)或W(西)
<12> 模式指示(僅NMEA0183 3.00版本輸出,A=自主定位,D=差分,E=估算,N=資料無效)
我們提取1~9九條資訊。用一個結構體存放這些資訊:
typedef struct gps_info
{
char utc_time[BUF_SIZE];
char status;
float latitude_value;
char latitude;
float longtitude_value;
char longtitude;
float speed;
float azimuth_angle;
char utc_data[BUF_SIZE];
}GPS_INFO;
typedef struct gps_info
{
char utc_time[BUF_SIZE];
char status;
float latitude_value;
char latitude;
float longtitude_value;
char longtitude;
float speed;
float azimuth_angle;
char utc_data[BUF_SIZE];
}GPS_INFO;
因為每一個欄位之間都是以逗號間隔開的,所以我們可以利用%[^,]來分割字串,這樣用sscanf函數就可以實現對有效資訊的提取:
sscanf(wellhandled_string,"$GPRMC,%[^,],%c,%f,%c,%f,%c,%f,%f,%[^,]",
rmc_info->utc_time,\
&(rmc_info->status),&(rmc_info->latitude_value),&(rmc_info->latitude),\
&(rmc_info->longtitude_value),&(rmc_info->longtitude),&(rmc_info->speed),\
&(rmc_info->azimuth_angle),\
rmc_info->utc_data );這個函數執行後,列印出的保存在struct gps_info結構體裏的資訊如下所示:
utc_time: 024813.640
status: A
latitude: N latitude value: 3158.460693
longtitude: E longtitude value: 11848.374023
speed: 10.050000
azimuth_angle: 324.269989
utc_data: 150706
sscanf(wellhandled_string,"$GPRMC,%[^,],%c,%f,%c,%f,%c,%f,%f,%[^,]",
rmc_info->utc_time,\
&(rmc_info->status),&(rmc_info->latitude_value),&(rmc_info->latitude),\
&(rmc_info->longtitude_value),&(rmc_info->longtitude),&(rmc_info->speed),\
&(rmc_info->azimuth_angle),\
rmc_info->utc_data );這個函數執行後,列印出的保存在struct gps_info結構體裏的資訊如下所示:
utc_time: 024813.640
status: A
latitude: N latitude value: 3158.460693
longtitude: E longtitude value: 11848.374023
speed: 10.050000
azimuth_angle: 324.269989
utc_data: 150706
可見,利用好sscanf函數,可以讓我們可以很高效的處理字串。
sscanf 教學請看
http://ccckmit.wikidot.com/cp:sscanf
參考資料
http://blog.csdn.net/xmxqiyan/article/details/9134025
http://www.embedu.org/Column/Column196.htm
http://blog.csdn.net/baishengjie/article/details/5294321
這裡有詳細的gps命令解釋跟各家gps廠命令範例
http://www.gpsinformation.org/dale/nmea.htm
gps 模擬器..可以模擬gps裝置 由虛擬串列埠 或 真實串列埠 輸出 pgs 語句作測試
沒有留言 :
張貼留言