一、前言
GPS/北斗模块基本上都是通过串口发送数据的,并且发送数据的频率是1Hz,发送的数据内容可以去搜索“GPS数据格式”,反正这个模块发回来的数据很多,其中经纬度数据是包含在这些数据里面的。并且模块发回来的全部都是字符串,因此从GPS/北斗模块获取经纬度,实际上就是一个字符串解析的过程。
比如:$GNRMC,143147.000,A,3413.64266,N,10851.97266,E,000.8,296.4,170821,,,A*7C
这一帧数据里面就包含了经纬度,但数据格式是ddmm.mmmmm格式,比如这帧数据里面,“3413.64266”这个数是34度加13.64266分,分到度是60进制,因此要把分转换成度需要把分除以60,意思是13.64266分等于13.64266 / 60 = 0.227378度,那么3413.64266这个数就是34.227378度。同理经度也是一样。
另外程序里还有一个坑,由于使用了atoi函数,使用这个函数需要包含stdlib.h,神奇的是在不包含stdlib.h的情况下编译程序也能通过,并且运行时也不会报错,但是atoi函数返回的结果是不对的。
二、代码
程序代码很简单,编译时需要将uart.c也加入编译,并且要加-lm参数。
1 /**
2 * filename: gps.c
3 * author: Suzkfly
4 * date: 2021-08-21
5 * platform: S3C2416
6 * 编译时要加-lm
7 */
8
9 #include <stdio.h>
10 #include <string.h>
11 #include <math.h>
12 #include <stdlib.h> /* 必须要包含stdlib.h,不然编译能通过,运行也不会报错,但atof函数运行的结果是错误的 */
13 #include "uart.h"
14
15 /**
16 * \brief 从GPS/北斗模块中得到经纬度
17 *
18 * \param[in] fd:打开的串口设备的文件描述符
19 * \param[out] p_lon:得到的经度
20 * \param[out] p_lat:得到的纬度
21 *
22 * \retval 成功返回0,失败返回-1
23 */
24 int get_gps (int fd, char *p_lon, char *p_lat)
25 {
26 char buf[256] = { 0 };
27 int ret = 0;
28 FILE *p_file = NULL;
29 char *p_tmp = NULL;
30 char lon_tmp[32] = { 0 };
31 char lat_tmp[32] = { 0 };
32 double d_lon = 0;
33 double d_lat = 0;
34 double d_tmp1 = 0;
35 double d_tmp2 = 0;
36
37 if ((p_lon == NULL) || (p_lat == NULL)) {
38 return -1;
39 }
40
41 /* 文件描述符转文件流指针,为了方便获取一行数据 */
42 p_file = fdopen(fd, "r");
43
44 while (1) {
45 p_tmp = fgets(buf, sizeof(buf), p_file); /* 获取一行数据 */
46 if (p_tmp == NULL) {
47 continue;
48 }
49
50 if (strstr(buf, "$GNGGA") || strstr(buf, "$GPGGA")) {
51 strtok(buf, ",");
52 strtok(NULL, ",");
53 p_tmp = strtok(NULL, ",");
54 strcpy(lat_tmp, p_tmp); /* 第3次strtok得到纬度(度分格式) */
55
56 strtok(NULL, ",");
57 p_tmp = strtok(NULL, ",");
58 strcpy(lon_tmp, p_tmp); /* 第5次strtok得到经度(度分格式) */
59 break;
60 } else if (strstr(buf, "$GNRMC") || strstr(buf, "$GPRMC")) {
61 strtok(buf, ",");
62 strtok(NULL, ",");
63 strtok(NULL, ",");
64 p_tmp = strtok(NULL, ",");
65 strcpy(lat_tmp, p_tmp); /* 第4次strtok得到纬度(度分格式) */
66
67 strtok(NULL, ",");
68 p_tmp = strtok(NULL, ",");
69 strcpy(lon_tmp, p_tmp); /* 第6次strtok得到经度(度分格式) */
70 break;
71 }
72 }
73
74 /* 将度分格式转换为度格式 */
75 d_lon = atof(lon_tmp);
76 d_lat = atof(lat_tmp);
77 d_lon /= 100;
78 d_lat /= 100;
79
80 d_tmp1 = floor(d_lon);
81 d_tmp2 = d_lon - d_tmp1;
82 d_tmp2 *= 100;
83 d_tmp2 /= 60;
84 d_lon = d_tmp1 + d_tmp2;
85
86 d_tmp1 = floor(d_lat);
87 d_tmp2 = d_lat - d_tmp1;
88 d_tmp2 *= 100;
89 d_tmp2 /= 60;
90 d_lat = d_tmp1 + d_tmp2;
91
92 sprintf(p_lon, "%f", d_lon);
93 sprintf(p_lat, "%f", d_lat);
94
95 return 0;
96 }
97
98 /**
99 * \brief example
100 */
101 int main(int argc, const char *argv[])
102 {
103 int fd = 0;
104 int ret = 0;
105 int pid = 0;
106 char buf[128] = { 0 };
107 int len = 0;
108 int i = 0;
109 char lon[32] = { 0 };
110 char lat[32] = { 0 };
111
112 /* 打开串口设备 */
113 fd = uart_open(UART_DEV_PATH);
114 if (fd < 0) {
115 printf("open %s failed\n", UART_DEV_PATH);
116 return -1;
117 }
118
119 /**
120 * 配置串口:
121 * 波特率:BAUD_RATE
122 * 数据位:8
123 * 校验 :无校验
124 * 停止位:1
125 * 流控 :无流控
126 */
127 ret = uart_set(fd, BAUD_RATE, 8, 'n', 1, 'n');
128 if (ret == -1) {
129 perror("uart set failed\n");
130 return -1;
131 }
132
133 /* 得到GPS/北斗模块的经纬度 */
134 ret = get_gps(fd, lon, lat);
135 if (ret != 0) {
136 perror("get_gps error\n");
137 return -1;
138 }
139
140 printf("Original Longitude = %s\n", lon);
141 printf("Original Latitude = %s\n", lat);
142
143 return 0;
144 }