单片机处理GPS模块信息

本文介绍了在单片机中处理GPS数据的过程,包括GPS信息的串口接收、GPRMC数据解析、经纬度计算以及两点间距离的理论和代码实现。还详细展示了如何解包GPS数据并存储在GPS_INFO结构体中,以便后续导航应用。
摘要由CSDN通过智能技术生成

1.单片机处理部分

预:GPS结构体

typedef struct
{
    int year;
    int month;
    int day;
    int hour;
    int minute;
    int second;
	int ms; 
} DATE_TIME;

typedef struct
{
	    int latitudeint;      //纬度
		int latitudefloat;
		int longitudeint;     //经度
	    int longitudefloat;
        double latitude_Degree;  //纬度,度
        double longitude_Degree; //经度,度
	    double latitude_DegreeStart;
	    double longitude_DegreeStart; //经度,度
	    double delta_latitude;  //纬度变换量
	    double delta_longitude;  //经度变化量
        float speed;          //地面速率
        float direction;      //地面航向
        int satellite;
	    bool informationflag;  //是否有新数据需要处理
	    int setorgdata;  //是否将当前点设置为起点
        u8 NS;
        u8 EW;
        DATE_TIME Time;
} GPS_INFO;

1.GPS信息获取(串口中断执行)

/**
* @description: GPS信号采集
* @Author: Aoiiix && Young
* @param: {type} void
* @tips : 使用串口命令使GPS只发送 GPRMC 最小定位信息
* @return:	1:接收成功   0:未完成接收
* 使用RawGPS全局变量存储数据
*/

u8 GPSGetInf(void)
{
  static u8 count = 0, ReciveFlag = 0,CountBackwardsFlag=0,CountBackwards=3;
  static char buff[80];
  char str;
  if (uart_index[GPSUART]->CSR & UART_CSR_RXAVL) // 读取到一个数据
  	str = (uint8)uart_index[GPSUART]->RDR;	  // 存储一个数据
  if (str == '$')								  //接收到"$",开始接收数据
  {
  	ReciveFlag = 1;
  	memset(buff, 0, sizeof buff);
  }
if (str == '*')      //接收到结束符,开始倒数录入校验和
  {
  	CountBackwardsFlag=1;
  }
  if (CountBackwardsFlag)
  	CountBackwards--;
  if (ReciveFlag == 1) //接受标志位置1开始记录数据
  {
  	buff[count] = str;
  	count++;
  }
  if (CountBackwards==0)
  {
  	ReciveFlag = 0;
  	count = 0;
  	RawGPS = buff;
  	CountBackwards=3;
  	CountBackwardsFlag=0;
  	ReciveFlag=0;
  	GPSInfNode.informationflag = 1;
  }
  return 1;
}

2.由经纬度计算两点距离

理论说明

已知两点经纬度计算距离的具体例子球是一个近乎标准的椭球体,它的赤道半径为6378.140千米,极半径为 6356.755千米,平均半径6371.004千米。如果我们假设地球是一个完美的球体,那么它的半径就是地球的平均半径,记为R。如果以0度经线为基 准,那么根据地球表面任意两点的经纬度就可以计算出这两点间的地表距离(这里忽略地球表面地形对计算带来的误差,仅仅是理论上的估算值)。设第一点A的经 纬度为(LonA, LatA),第二点B的经纬度为(LonB, LatB),按照0度经线的基准,东经取经度的正值(Longitude),西经取经度负值(-Longitude),北纬取90-纬度值(90- Latitude),南纬取90+纬度值(90+Latitude),则经过上述处理过后的两点被计为(MLonA, MLatA)和(MLonB, MLatB)。那么根据三角推导,可以得到计算两点距离的如下公式:C=sin(MLatA)*sin(MLatB)*cos(MLonA-MLonB)+cos(MLatA)*cos(MLatB)Distance=R*Arccos(\C)*Pi/180这里,R和Distance单位是相同,如果是采用6371.004千米作为半径,那么Distance就是千米为单位,如果要使用其他单位,比如mile,还需要做单位换算,1千米=0.621371192mile,如果仅对经度作正负的处理,而不对纬度作90-Latitude(假设都是北半球,南半球只有澳洲具有应用意义)的处理,那么公式将是:C=sin(LatA)*sin(LatB)+cos(LatA)cos(LatB)cos(MLonA-MLonB)Distance=RArccos(\C)Pi/180以上通过简单的三角变换就可以推出。如果三角函数的输入和输出都采用弧度值,那么公式还可以写作:C=sin(LatAPi/180)sin(LatBPi/180)+cos(LatAPi/180)cos(LatBPi/180)cos((MLonA-MLonB)Pi/180)Distance=RArccos(\C)Pi/180也就是:C=sin(LatA/57.2958)sin(LatB/57.2958)+cos(LatA/57.2958)cos(LatB/57.2958)cos((MLonA-MLonB)/57.2958)Distance=RArccos(\C)=6371.004Arccos(\C) kilometer=0.6213711926371.004Arccos(\C)mile=3958.758349716768Arccos(\C) mile

代码实现

```C

/**

  • @description: 计算两点距离

  • @Author: Young

  • @param: 纬度1,经度1,纬度2,经度2

  • @return: 两点距离
    */
    double GetDistant (double latitude1, double longitude1, double latitude2, double longitude2)
    {
    const double EARTH_RADIUS = 6378137;//地球半径(单位:m)
    double rad_latitude1;
    double rad_latitude2;
    double rad_longitude1;
    double rad_longitude2;
    double distance;
    double a;
    double b;

    rad_latitude1 = Angle2Rad(latitude1);//根据角度计算弧度
    rad_latitude2 = Angle2Rad(latitude2);
    rad_longitude1 = Angle2Rad(longitude1);
    rad_longitude2 = Angle2Rad(longitude2);

    a = rad_latitude1 - rad_latitude2;
    b = rad_longitude1 - rad_longitude2;

    distance = 2 * asin(sqrt(pow(sin(a/2),2) + cos(rad_latitude1)cos(rad_latitude2)pow(sin(b/2),2)));
    distance = distance * EARTH_RADIUS;
    if (latitude1 < latitude2)
    return -1
    distance;
    if (longitude1<longitude2)
    return -1
    distance;
    return distance;
    }

  亲测十分好用...这个函数是后面使用GPS导航的关键

3.GPS信息解包

一些预处理函数
/**
 * @description: GPS数据处理函数
 * @Author: Aoiiix && Young
 * @param: {type} u8* buf     位置数组
 *                char* line  信息指针
 * @return:	NULL
 *  buf[n] : 第n个','位置
 */

void FindComma(u8 *buf, char *line)
{
	const char *p = line;
	u8 times = 0;
	buf++;
	while (*(p + times) != '\0')
	{
		if (*(p + times) == ',')
		{
			*buf = times;
			buf++;
			times++;
		}
		else
			times++;
	}
}
/**
 * @description: 字符串转浮点型数据,整散分离
 * @Author: Aoiiix && Young
 * @param: {type} char* buf     信息指针
 *                u8* index   逗号下标数组
 *                u8  第几个逗号后的数据
 *                bool intflag 整型或浮点型
 *                int* num     数据接收数组 0为整数 1为小数
 * @return:	     void
 *
 */
void GetNumberSeparate(char *buf, u8 *index, u8 n ,int* num)
{
	u8 data[20];
	u8 count = 0;
	u8 i;
	u8 point;
	for (i = 0; i <*(index + n + 1) - *(index + n); i++)
	{
		if (i == 0)
			while (*(buf + *(index + n) + i + 1) == '0') //去掉高位补位0
				i++;
		*(data + count) = *(buf + *(index + n) + i + 1);
		if (*(data + count) == '.')
			point = count;
		count++;
	}
	for (i = 0; i < count-1; i++)
	{
		if (i < point)
			*num = *num * 10 + (*(data + i) - '0');
		if (i>point)
			*(num+1) = *(num+1) * 10 + (*(data + i) - '0');
	}

}
/**
 * @description: 字符串转浮点型
 * @Author: Aoiiix && Young
 * @param: {type} char* buf     信息指针
 *                u8* index   逗号下标数组
 *                u8  第几个逗号后的数据
 * @return:	     num
 *
 */
float GetNumber(char *buf, u8 *index, u8 n)
{
	u8 data[10];
	u8 count = 0;
	volatile u8 i;
	int point = 0;
	bool flag = 0;
	float num = 0;
	for (i = 0; i < *(index + n + 1) - *(index + n); i++)
	{
		if (i == 0)
		{while (*(buf + *(index + n) + i + 1) == '0'&&*(buf + *(index + n) + i + 2)!='.') //去掉高位补位0
				i++;
			if (*(buf + *(index + n) + i + 1) == '-')
			{
				i++;
				flag = 1;
			}
		}
		*(data + count) = *(buf + *(index + n) + i + 1);
		if (*(data + count) == '.')
			point = count;
		count++;
	}
	if (point == 0)  //没有小数点.
	{
		 point = 255;
	}
	for (i = 0; i < count-1; i++)
	{
		if (i < point)
			num = num * 10 + (*(data + i) - '0');
		if (i > point)
			num += (*(data + i) - '0') * myMath_power(0.1, i - point);
	}
	if (flag)
	return -num;
	else
		return num;
}
数据解包函数
/**
* @description: GPS数据解包
* @Author: Aoiiix && Young
* @param: {type} GPS_INFO* GPS_INFO     GPS信息结构体
* @return:	  1:信息有效解包成功  0:数据校验无效
*
*/
bool GetMainGPS(GPS_INFO *GPS_INFO,bool flag)
{
  if (flag==0)
  	return 0;
  u8 index[30]={0};
  double degree;
  int time=0,num[2]={0};
  u8  Checksum;                           //校验和
  u8  Result;                             //数据的校验和
  FindComma(index, RawGPS);
  char str = *(RawGPS + index[2] + 1);
  if (*(RawGPS + index[2] + 1) == 'A')    //一次检验数据是否有效
  	 ;
  else
  {
    GPSInfNode.Time.ms=(int)(GetNumber(RawGPS, index,1)*10)%10;
  	return 0;
  }
  str = *(RawGPS+index[12]+5);
  if (str>='A')
  	Checksum=(str-'7')*16;
  else
  	Checksum=(str-'0')*16;
  str = *(RawGPS+index[12]+6);
  if (str>='A')
  	Checksum+=(str-'7');
  else
  	Checksum+=(str-'0');
  Result = *(RawGPS+1);
  while (*(RawGPS+2+time)!='*')
  {
  	Result^=*(RawGPS+2+time);
  	time++;
  }	
  if (Result==Checksum)  
  {

  GetNumberSeparate(RawGPS, index,3,num);
  GPSInfNode.latitudeint    = num[0];
  GPSInfNode.latitudefloat  = num[1];
  
  memset(num, 0, sizeof num); 
  
  GetNumberSeparate(RawGPS, index,5,num);
  	
  GPSInfNode.longitudeint   = num[0];
  GPSInfNode.longitudefloat = num[1];
  
  degree = 	GPSInfNode.latitudeint/100 + (GPSInfNode.latitudeint%100)/60.0 + 
                                   (double)GPSInfNode.latitudefloat/100000/60;
  
  GPSInfNode.delta_latitude = degree - GPSInfNode.latitude_Degree; 
  	
  if (degree >30)
  GPSInfNode.latitude_Degree = degree;
  
  degree = GPSInfNode.longitudeint/100 + (GPSInfNode.longitudeint%100)/60.0 + 
                                   (double)GPSInfNode.longitudefloat/100000/60;	

  GPSInfNode.delta_longitude = GPSInfNode.longitude_Degree - degree;
  
  if (degree > 120)
  GPSInfNode.longitude_Degree = degree;

  GPSInfNode.NS          = *(RawGPS+index[4]+1);
  GPSInfNode.EW          = *(RawGPS+index[6]+1);
  
  memset(num, 0, sizeof num);
  
  GetNumberSeparate(RawGPS, index,8,num);
  
  GPSInfNode.direction   = num[0] + num[1]*0.01;
  
  time=(int)GetNumber(RawGPS, index,1);
  
  GPSInfNode.Time.hour   = time/10000;
  GPSInfNode.Time.hour   = GPSInfNode.Time.hour+8-(GPSInfNode.Time.hour+8>=24)*24;
  GPSInfNode.Time.minute = (time/100)%100;
  GPSInfNode.Time.second = time%100;
  GPSInfNode.speed       = GetNumber(RawGPS, index, 7);
  
  GPSInfNode.Time.ms=(int)(GetNumber(RawGPS, index,1)*10)%10;  //把ms放在最后,拿ms是否变化作为接收并完成一次转化的标志
  
  GPSInfNode.informationflag = 0;  //清除GPS信息标志位


  
  return 1;
  }
  else
  	return 0;
}
  • 21
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Gedou_Suguru

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值