通信接口

GPS接收机串行通信标准摘要 参考NMEA-0183

字号+ 作者:duino123.com 来源:未知 2016-06-12 14:52 我要评论( )

GPS接收机串行通信标准摘要参考NMEA-0183 美国国家海洋电子协会(NMEAThe NationalMarine Electronics Association) 为了在不同的GPS导航设备中建立统一的RTCM(海事无线电技术委员会)标准,先后制定了NMEA-0180、0182和0183三个标准。0183可以认为是前两种的

GPS接收机串行通信标准摘要参考NMEA-0183

美国国家海洋电子协会(NMEA—The NationalMarine Electronics Association) 为了在不同的GPS导航设备中建立统一的RTCM(海事无线电技术委员会)标准,先后制定了NMEA-0180、0182和0183三个标准。0183可以认为是前两种的升级,也是目前使用最为广泛的一种。目前广泛采用的是Ver 2.00版本。现在除少数GPS接收机外,几乎所有的GPS接收机均采用了这一格式。

接口电平

符合NMEAO183标准的GPS接收机的硬件接口能够兼容计算机的RS-232C协议串口,然而,严格来说NMEA标准不是RS-232C,规范推荐依照EIA422(也称为RS-422)。
EIA-422是利用导线之间的信号电压差来传输信号的,其每个通道要用两条信号线,一条是逻辑“1”,~条是逻辑“0”,通过传输线驱动器和传输线接收器实现逻辑电平和电位差之间的转换,一般允许驱动器输出为±2V~ ±6V 。
标准RS-232C采用负逻辑,即逻辑“1”表示-5V~ -15v,逻辑“0”表示+5V~+15V,利用传输信号线和信号地之间的电压差进行传输。
虽然存在区别,但在实际使用中,如果只是接收GPS的输出,则只需GPS数据输出线和信号地线同计算机的Rs232C输入线相连(这个方法我并没有试验过,是从别的地方听来的,有兴趣有条件的兄弟可以动手实验一下,不过后果自负哦!呵呵)。
NMEA通讯协议所定义的标准通讯接口参数为:
波特率:4800bit/s;
数据位:8位;
停止位:1位;
奇偶校验:无;
 

输出语句格式

NMEA通讯协议所规定的通讯语句都已是以ASCII码为基础的,NMEA-0183协议语句的数据格式如下:“$”为语句起始标志;“,”为域分隔符;“ *”为校验和识别符,其后面的两位数为校验和,代表了“$”和“*”之间所有字符的按位异或值(不包括这两个字符);回车换行为终止符,所有的语句必须以此来结束,也就是ASCII 字符的0x0D和0x0A。
接收机可能发送很多类型的语句,而我们需要的可能只是某些语句中的几个字段。因此就需要对接收到的数据进行解析,取得所需的信息。另外,可能会由于小数点位数不同等原因,语句的长度是可变的,因而分离感兴趣的信息时,不能按照该信息在语句中所处的字符位置来查找,只能依据逗号分隔符,这一点在数据提取的过程中非常重要。
GPS 接收OEM 板的型号甚多、性能各异,但它们的GPS定位信息串行输出格式大多采用美国国家海洋电子协会制定的NMEA-0183 通信标准格式。其输出数据采用的是ASCII码,内容包含了纬度、经度、高度、速度、日期、时间、航向以及卫星状况等信息,常用语句有6 种,包括GGA、GLL、GSA、GSV、RMC 和 VTG。我们也可以通过GPS 专用设置软件或普通的串口调试软件发送相应的命令语句给OEM 板,把GPS OEM 板设置为每隔若干毫秒发送哪种或哪几种NMEA 语句,然后该OEM 板将这些设置参数存储到板上的EEPROM 芯片内,此后该OEM 板将按照这些设置每隔相应的毫秒数发送出一个或几个GPS 输出NMEA 语句。根据不同的应用需要,设置选择不同的输出记录语句以及它们的发送时间间隔,如本系统我们只关心其时间、经纬度、海拔高度、地面速度信息以及卫星使用数信息,因而可只选用GGA,VTG记录语句并设成每1s发送一次。不过须注意,这些设置信息只在系统本次上电,并设置后方有效,在下次重新上电时需重新设置。
•   $GPAAM - Waypoint Arrival Alarm 
•   $GPBOD - Bearing, Origin to Destination 
•   $GPBWW - Bearing, Waypoint to Waypoint 
•   $GPGGA - Global Positioning System Fix Data 
•   $GPGLL - Geographic Position, Latitude/Longitude 
•   $GPGSA - GPS DOP and Active Satellites 
•   $GPGST - GPS Pseudorange Noise Statistics 
•   $GPGSV - GPS Satellites in View 
•   $GPHDG - Heading, Deviation & Variation 
•   $GPHDT - Heading, True 
•   $GPRMB - Recommended Minimum Navigation Information 
•   $GPRMC - Recommended Minimum Specific GPS/TRANSIT Data 
•   $GPRTE - Routes 
•   $GPVTG - Track Made Good and Ground Speed 
•   $GPWCV - Waypoint Closure Velocity 
•   $GPWNC - Distance, Waypoint to Waypoint 
•   $GPWPL - Waypoint Location 
•   $GPXTE - Cross-Track Error, Measured 
•   $GPXTR - Cross-Track Error, Dead Reckoning 
•   $GPZDA - UTC Date/Time and Local Time Zone Offset 
•   $GPZFO - UTC and Time from Origin Waypoint 
•   $GPZTG - UTC and Time to Destination Waypoint 
 

$GPGGA固定数据输出语句

这是一帧GPS定位的主要数据,也是使用最广的数据。
$GPGGA 语句包括17个字段:语句标识头,世界时间,纬度,纬度半球,经度,经度半球,定位质量指示,使用卫星数量,水平精确度,海拔高度,高度单位,大地水准面高度,高度单位,差分GPS数据期限,差分参考基站标号,校验和结束标记(用回车符<CR>和换行符<LF>),分别用14个逗号进行分隔。该数据帧的结构及各字段释义如下:
$GPGGA,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,M,<10>,M,<11>,<12>*xx<CR><LF>
$GPGGA:起始引导符及语句格式说明(本句为GPS定位数据);
<1>   UTC时间,格式为hhmmss.sss;
<2>   纬度,格式为ddmm.mmmm(第一位是零也将传送);
<3>   纬度半球,N或S(北纬或南纬)
<4>   经度,格式为dddmm.mmmm(第一位零也将传送);
<5>   经度半球,E或W(东经或西经)
<6>   定位质量指示,0=定位无效,1=定位有效;
<7>   使用卫星数量,从00到12(第一个零也将传送)
<8>   水平精确度,0.5到99.9
<9>   天线离海平面的高度,-9999.9到9999.9米
M 指单位米
<10> 大地水准面高度,-9999.9到9999.9米
M 指单位米
<11> 差分GPS数据期限(RTCM SC-104),最后设立RTCM传送的秒数量
<12>   差分参考基站标号,从0000到1023(首位0也将传送)。
* 语句结束标志符
xx 从$开始到*之间的所有ASCII码的异或校验和
<CR> 回车
<LF> 换行
例子
$GPGGA,050901,3931.4449,N,11643.5123,E,1,07,1.4,76.2,M,-7.0,M,,*65  
其标准格式为:$GPGGA,(1),(2),(3),(4),(5),(6),(7),(8),(9),M,(10),M,(11),(12)*hh(CR)(LF)
各部分所对应的含义为:  
(1)定位UTC时间:05时09分01秒
(2)纬度(格式ddmm.mmmm:即dd度,mm.mmmm分);
(3)N/S(北纬或南纬):北纬39度31.4449分;
(4)经度(格式dddmm.mmmm:即ddd度,mm.mmmm分);
(5)E/W(东经或西经):东经116度43.5123分;
(6)质量因子(0=没有定位,1=实时GPS,2=差分GPS):1=实时GPS;
(7)可使用的卫星数(0~8):可使用的卫星数=07;
(8)水平精度因子(1.0~99.9);水平精度因子=1.4;
(9)天线高程(海平面,-9999.9~99999.9,单位:m);天线高程=76.2m);  
(10)大地椭球面相对海平面的高度(-999.9~9999.9,单位:m):-7.0m;  
(11)差分GPS数据年龄,实时GPS时无:无;  
(12)差分基准站号(0000~1023),实时GPS时无:无;  
*总和校验域;
hh 总和校验数:65  
(CR)(LF)回车,换行。   
 

$GPGSA当前卫星信息

$GPGSA,<1>,<2>,<3>,<3>,,,,,<3>,<3>,<3>,<4>,<5>,<6>,<7>
<1>模式 :M = 手动, A = 自动。
<2>定位型式 1 = 未定位, 2 = 二维定位, 3 = 三维定位。
<3>PRN 数字:01 至 32 表天空使用中的卫星编号,最多可接收12颗卫星信息。
<4> PDOP位置精度因子(0.5~99.9)
<5> HDOP水平精度因子(0.5~99.9)
<6> VDOP垂直精度因子(0.5~99.9)
<7> Checksum.(检查位).

$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=数据无效)

$GPVTG地面速度信息

$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=数据无效)
GPS 数据格式

$GPGSV可视卫星状态

$GPGSV, <1>,<2>,<3>,<4>,<5>,<6>,<7>,?<4>,<5>,<6>,<7>,<8><CR><LF>
1) 天空中收到讯号的卫星总数。
2) 定位的卫星总数。
3) 天空中的卫星总数,00 至 12。
4) 卫星编号, 01 至 32。
5) 卫星仰角, OO 至 90 度。
6) 卫星方位角, OOO 至 359 度。实际值。
7) 讯号噪声比(C/No), 00 至 99 dB;无表未接收到讯号。
8) Checksum.(检查位).
第<4>,<5>,<6>,<7>项个别卫星会重复出现,每行最多有四颗卫星。其余卫星信息会于次一行出现,若未使用,这些字段会空白。
例子
$GPGSV,2,1,08,06,33,240,45,10,36,074,47,16,21,078,44,17,36,313,42*78
各部分含义为:  
(1)总的GSV语句电文数;2;
(2)当前GSV语句号:1;  
(3)可视卫星总数:08;  
(4)卫星号:06;  
(5)仰角(00~90度):33度;  
(6)方位角(000~359度):240度;  
(7)信噪比(00~99dB):45dB(后面依次为第10,16,17号卫星的信息);  
*总和校验域;
hh 总和校验数:78;  
(CR)(LF)回车,换行。  
 

$P

除标准语句外,NMEAO183规范还允许个别厂商定义私有的语句格式,这些语句以“$P”开始,然后是三个字符长度的厂商识别号,跟着是厂商定义的数据,接下来的数据格式与标准格式相同。
如Garmin的PGRME私有格式如下:
$PGRME,8.9,M,6.1,M,10.8,M*11
其中,“P”代表私有格式,“GRM”是Garmin的代码,“E”表示语句类型。

最简化的GPS解析程序:

#define GPRMC_TERM "$GPRMC,"		//定义要解析的指令,因为这条指令包含定位和时间信息

char nmeaSentence[68];
String latitude;		//纬度
String longitude;		//经度
String lndSpeed;		//速度
String gpsTime;			//UTC时间,本初子午线经度0度的时间,和北京时间差8小时
String beiJingTime;		//北京时间

#define GpsSerial Serial1
#define DebugSerial Serial

void setup()	//初始化内容
{
  GpsSerial.begin(9600);			//定义波特率9600,和我们店铺的GPS模块输出的波特率一致
  DebugSerial.begin(9600);
  DebugSerial.println("Wating to get gps data...");
}

void loop()		//主循环
{

  // For one second we parse GPS data and report some key values
  for (unsigned long start = millis(); millis() - start < 1000;)	//一秒钟内不停扫描GPS信息
  {
    while (GpsSerial.available())	//串口获取到数据开始解析
    {
      char c = GpsSerial.read();	//读取一个字节获取的数据

      switch(c)					//判断该字节的值
      {
      case '$':					//若是$,则说明是一帧数据的开始
        GpsSerial.readBytesUntil('*', nmeaSentence, 67);		//读取接下来的数据,存放在nmeaSentence字符数组中,最大存放67个字节
        latitude = parseGprmcLat(nmeaSentence);	//获取纬度值
        longitude = parseGprmcLon(nmeaSentence);//获取经度值
        lndSpeed = parseGprmcSpeed(nmeaSentence);//获取速度值
        gpsTime = parseGprmcTime(nmeaSentence);//获取GPS时间


        if(latitude > "")		//当不是空时候打印输出
        {
          DebugSerial.println("------------------------------------");
          DebugSerial.println("latitude: " + latitude);
        }

        if(longitude > "")		//当不是空时候打印输出
        {
          DebugSerial.println("longitude: " + longitude);
        }  

        if(lndSpeed > "")		//当不是空时候打印输出
        {
          DebugSerial.println("Speed (knots): " + lndSpeed);
        }

        if(gpsTime > "")		//当不是空时候打印输出
        {
          DebugSerial.println("gpsTime: " + gpsTime);
          beiJingTime = getBeiJingTime(gpsTime);	//获取北京时间 
          DebugSerial.println("beiJingTime: " + beiJingTime);        
        }		
      }
    }
  }
}

String getBeiJingTime(String s)
{
  int hour = s.substring(0,2).toInt();
  int minute = s.substring(2,4).toInt();
  int second = s.substring(4,6).toInt();

  hour += 8;

  if(hour > 24)
    hour -= 24;
  s = String(hour) + ":" + String(minute) + ":" + String(second);
  return s;
}

//Parse GPRMC NMEA sentence data from String
//String must be GPRMC or no data will be parsed
//Return Latitude
String parseGprmcLat(String s)
{
  int pLoc = 0; //paramater location pointer
  int lEndLoc = 0; //lat parameter end location
  int dEndLoc = 0; //direction parameter end location
  String lat;
  /*make sure that we are parsing the GPRMC string. 
   Found that setting s.substring(0,5) == "GPRMC" caused a FALSE.
   There seemed to be a 0x0D and 0x00 character at the end. */
  if(s.substring(0,4) == "GPRM")
  {
    //Serial.println(s);
    for(int i = 0; i < 5; i++)
    {
      if(i < 3) 
      {
        pLoc = s.indexOf(',', pLoc+1);
        /*Serial.print("i < 3, pLoc: ");
         Serial.print(pLoc);
         Serial.print(", ");
         Serial.println(i);*/
      }
      if(i == 3)
      {
        lEndLoc = s.indexOf(',', pLoc+1);
        lat = s.substring(pLoc+1, lEndLoc);
        /*Serial.print("i = 3, pLoc: ");
         Serial.println(pLoc);
         Serial.print("lEndLoc: ");
         Serial.println(lEndLoc);*/
      }
      else
      {
        dEndLoc = s.indexOf(',', lEndLoc+1);
        lat = lat + " " + s.substring(lEndLoc+1, dEndLoc);
        /*Serial.print("i = 4, lEndLoc: ");
         Serial.println(lEndLoc);
         Serial.print("dEndLoc: ");
         Serial.println(dEndLoc);*/
      }
    }
    return lat; 
  }
  //}
  //}
}

//Parse GPRMC NMEA sentence data from String
//String must be GPRMC or no data will be parsed
//Return Longitude
String parseGprmcLon(String s)
{
  int pLoc = 0; //paramater location pointer
  int lEndLoc = 0; //lat parameter end location
  int dEndLoc = 0; //direction parameter end location
  String lon;

  /*make sure that we are parsing the GPRMC string. 
   Found that setting s.substring(0,5) == "GPRMC" caused a FALSE.
   There seemed to be a 0x0D and 0x00 character at the end. */
  if(s.substring(0,4) == "GPRM")
  {
    //Serial.println(s);
    for(int i = 0; i < 7; i++)
    {
      if(i < 5) 
      {
        pLoc = s.indexOf(',', pLoc+1);
        /*Serial.print("i < 3, pLoc: ");
         Serial.print(pLoc);
         Serial.print(", ");
         Serial.println(i);*/
      }
      if(i == 5)
      {
        lEndLoc = s.indexOf(',', pLoc+1);
        lon = s.substring(pLoc+1, lEndLoc);
        /*Serial.print("i = 3, pLoc: ");
         Serial.println(pLoc);
         Serial.print("lEndLoc: ");
         Serial.println(lEndLoc);*/
      }
      else
      {
        dEndLoc = s.indexOf(',', lEndLoc+1);
        lon = lon + " " + s.substring(lEndLoc+1, dEndLoc);
        /*Serial.print("i = 4, lEndLoc: ");
         Serial.println(lEndLoc);
         Serial.print("dEndLoc: ");
         Serial.println(dEndLoc);*/
      }
    }
    return lon; 
  }
}

//Parse GPRMC NMEA sentence data from String
//String must be GPRMC or no data will be parsed
//Return Longitude
String parseGprmcSpeed(String s)
{
  int pLoc = 0; //paramater location pointer
  int lEndLoc = 0; //lat parameter end location
  int dEndLoc = 0; //direction parameter end location
  String lndSpeed;

  /*make sure that we are parsing the GPRMC string. 
   Found that setting s.substring(0,5) == "GPRMC" caused a FALSE.
   There seemed to be a 0x0D and 0x00 character at the end. */
  if(s.substring(0,4) == "GPRM")
  {
    //Serial.println(s);
    for(int i = 0; i < 8; i++)
    {
      if(i < 7) 
      {
        pLoc = s.indexOf(',', pLoc+1);
        /*Serial.print("i < 8, pLoc: ");
         Serial.print(pLoc);
         Serial.print(", ");
         Serial.println(i);*/
      }
      else
      {
        lEndLoc = s.indexOf(',', pLoc+1);
        lndSpeed = s.substring(pLoc+1, lEndLoc);
        /*Serial.print("i = 8, pLoc: ");
         Serial.println(pLoc);
         Serial.print("lEndLoc: ");
         Serial.println(lEndLoc);*/
      }
    }
    return lndSpeed; 
  }
}


//Parse GPRMC NMEA sentence data from String
//String must be GPRMC or no data will be parsed
//Return Longitude
String parseGprmcTime(String s)
{
  int pLoc = 0; //paramater location pointer
  int lEndLoc = 0; //lat parameter end location
  int dEndLoc = 0; //direction parameter end location
  String gpsTime;

  /*make sure that we are parsing the GPRMC string. 
   Found that setting s.substring(0,5) == "GPRMC" caused a FALSE.
   There seemed to be a 0x0D and 0x00 character at the end. */
  if(s.substring(0,4) == "GPRM")
  {
    //Serial.println(s);
    for(int i = 0; i < 2; i++)
    {
      if(i < 1) 
      {
        pLoc = s.indexOf(',', pLoc+1);
        /*Serial.print("i < 8, pLoc: ");
         Serial.print(pLoc);
         Serial.print(", ");
         Serial.println(i);*/
      }
      else
      {
        lEndLoc = s.indexOf(',', pLoc+1);
        gpsTime = s.substring(pLoc+1, lEndLoc);
        /*Serial.print("i = 8, pLoc: ");
         Serial.println(pLoc);
         Serial.print("lEndLoc: ");
         Serial.println(lEndLoc);*/
      }
    }
    return gpsTime; 
  }
}

// Turn char[] array into String object
String charToString(char *c)
{

  String val = "";

  for(int i = 0; i <= sizeof(c); i++) 
  {
    val = val + c[i];
  }

  return val;
}


如需更详细资料,如有好资源分享,请加入QQ交流群:214461008

欢迎阅读,欢迎转载,不用注明出处!本站只提供入门和进阶资料,做您学习的小伙伴!

老司机也欢迎光临指导!有好内容请加群:214461008 不要看声明了,看内容吧!

相关文章
  • SPI模块官方手册

    SPI模块官方手册

    2016-05-31 15:40

网友点评
Arduino