一、前言
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參數。uart.c參見我的另一篇博客:linux串口編程
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 }