范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

分享一种有趣的数据解析方法

  本篇笔记是一篇开发小结,总结GPS数据的接收、解析示例,以实例为基础分享一些思考过程: GPS数据协议
  常用的GPS模块大多采用NMEA-0183 协议,目前业已成了GPS导航设备统一的RTCM(Radio Technical Commission for Maritime services)标准协议。NMEA-0183 是美国国家海洋电子协会(National Marine Electronics Association)所指定的标准规格,这一标准制订所有航海电子仪器间的通讯标准,其中包含传输资料的格式以及传输资料的通讯协议。
  协议采用  ASCII 码 来传递 GPS 定位信息,我们称之为帧。
  帧格式形如: $aaccc,ddd,ddd,…,ddd*hh(CR)(LF)
  GPS帧数据种类大致如下:
  实际应用中,并不是所有数据都完全用得上,我们可以根据需要选择所需要的数据。
  下面我们以 $GPGGA 数据为例分享接收、解析方法。
  $GPGGA 语句的基本格式如下: $GPGGA,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,<10>,<11>,<12>,<13>,<14>*hh
  举例如下: $PGGA,082006.000,3852.9276,N,11527.4283,E,1,08,1.0,20.6,M,,,,0000*35GPS数据接收
  GPS模块使用串口通信,在解析之前当然需要先接收数据。我这里是在嵌入式Linux平台下做的接收,读串口的接口如: int uart_read(void *data, int data_len, long time_out);
  下面分享我在实际应用中的三种接收方法: 方法一:粗略法
  为了能快速验证数据解析、跑通整个过程,可以先使用粗略的方法获取数据。粗略法我们可以先不用考虑一帧数据的实际字节数,我们先大致设置一个用于解析的缓冲数组,如: char rx_gps_data[512];
  uart_read每次读到的字节数与线程挂起时间有关,粗略法我们大致设置一个串口接收缓冲数组,如: char uart_rx_buf[64];
  这时候需要把每次收到的uart_rx_buf里的内容自己拼接一下,存放到rx_gps_data中,再去做解析。
  粗略法可以用于快速验证数据解析、跑通整个过程,缺点就是uart_rx_buf、rx_gps_data设置得不够合理的话可能会破坏掉大量的数据帧。
  一般我都比较习惯地先快速调通整个流程,再慢慢做优化。 方法二:状态机法
  上面的粗略法可能会破坏掉一些数据帧,另外,代码结构可能不够清晰。针对这些问题做改进,使用状态机来接收。一字节一字节地接收,接收完完整一帧数据之后再去做解析。
  代码如: // GGA所有状态(GGA数据示例:$GPGGA,023543.00,2308.28715,N,11322.09875,E,1,06,1.49,41.6,M,-5.3,M,,*7D) #define GGA_STATE_START     0  // $ #define GGA_STATE_HEAD1_G   1  // G #define GGA_STATE_HEAD2_P   2  // P #define GGA_STATE_HEAD3_G   3  // G #define GGA_STATE_HEAD4_G   4  // G #define GGA_STATE_HEAD5_A   5  // A #define GGA_STATE_DATA      6  // ,023543.00,2308.28715,N,11322.09875,E,1,06,1.49,41.6,M,-5.3,M,,* #define GGA_STATE_CHECK0    7  // 7 #define GGA_STATE_CHECK1    8  // D  static uint16_t gga_len = 0; static uint8_t gga_state = GGA_STATE_START; static void gps_gga_data_get(char in_data) {     switch (gga_state)     {         case GGA_STATE_START:             if ("#39; == in_data)             {                 gga_len = 0;                 memset(rx_gps_gga_data, 0, GGA_DATA_MAX_LEN);                 rx_gps_gga_data[gga_len++] = in_data;                 gga_state = GGA_STATE_HEAD1_G;             }             else             {                 gga_state = GGA_STATE_START;             }             break;                      case GGA_STATE_HEAD1_G:             if ("G" == in_data)             {                 rx_gps_gga_data[gga_len++] = in_data;                 gga_state = GGA_STATE_HEAD2_P;             }             else             {                 gga_state = GGA_STATE_START;             }             break;                      case GGA_STATE_HEAD2_P:             if ("P" == in_data)             {                 rx_gps_gga_data[gga_len++] = in_data;                 gga_state = GGA_STATE_HEAD3_G;             }             else             {                 gga_state = GGA_STATE_START;             }             break;                      case GGA_STATE_HEAD3_G:             if ("G" == in_data)             {                 rx_gps_gga_data[gga_len++] = in_data;                 gga_state = GGA_STATE_HEAD4_G;             }             else             {                 gga_state = GGA_STATE_START;             }             break;                      case GGA_STATE_HEAD4_G:             if ("G" == in_data)             {                 rx_gps_gga_data[gga_len++] = in_data;                 gga_state = GGA_STATE_HEAD5_A;             }             else             {                 gga_state = GGA_STATE_START;             }             break;                      case GGA_STATE_HEAD5_A:             if ("A" == in_data)             {                 rx_gps_gga_data[gga_len++] = in_data;                 gga_state = GGA_STATE_DATA;             }             else             {                 gga_state = GGA_STATE_START;             }             break;                      case GGA_STATE_DATA:             if ("*" == in_data)             {                 rx_gps_gga_data[gga_len++] = in_data;                 gga_state = GGA_STATE_CHECK0;             }             else             {                 rx_gps_gga_data[gga_len++] = in_data;                 if (gga_len > GGA_DATA_MAX_LEN)                 {                     gga_state = GGA_STATE_START;                 }                 else                 {                     gga_state = GGA_STATE_DATA;                 }             }             break;                      case GGA_STATE_CHECK0:             rx_gps_gga_data[gga_len++] = in_data;             gga_state = GGA_STATE_CHECK1;             break;                      case GGA_STATE_CHECK1:             rx_gps_gga_data[gga_len++] = in_data;             printf("gga data : %s ", rx_gps_gga_data);             gga_state = GGA_STATE_START;             break;                      default:             break;     } }
  这样就可以完整地接收到gga数据,每次走到GGA_STATE_CHECK1状态时的rx_gps_gga_data就是完整的gga数据,这时候就可以进行解析了,可以在这一步设置一个标志变量表明gga数据已经完全接收完毕,直到数据接收完毕了才做解析。
  这种方法虽然可以比较好地接收数据,在单片机下很好用。但是在这里,相同的线程挂起时间情况下,每次uart_read只获取一个字节,这样会损耗一定的接收效率,有点拆东墙补西墙的感觉。在我们这边的应用中,与算法所需的时序要求有冲突了,所以只能再想想其它方法。下面看看方法三。 方法三:时间戳法
  这种方法需要明确每一帧数据包含有什么数据,以及数据输出的频率是多少。在相同的线程挂起时间情况下,先把用于uart_read接收数据的buffer设置得稍微大一点,看每一次最多能读取到多少个字节得数据以及读完一帧数据需要读几次串口数据。然后我们可以通过时间来区分每一帧数据及每一包串口数据,该重新组包的就重新组包。
  例如:每帧数据间隔200ms,线程挂起时间10ms,一帧数据有130字节,一帧数据由1包、2包串口数据组成,可以通过时间戳来判断每一包之间是数据帧之间的间隔还是每一帧数据里的两个数据包之间的间隔,再做相应的逻辑处理即可很好地接收数据。 GPS数据解析
  gps数据怎么解析呢?
  方法可能很多,我们先看一下正点原子的解析方法:
  大概分为两步,第一步先获取逗号的位置确定某个需要解析的字段,然后再将相应字段的字符串数据转换成数字。
  这里分享一种简单实用的解析方法,思路与上面差不多,但是相对比较简单清晰些: static bool gps_gga_data_parse(st_gps_gga_def *out_data, char *in_data) {     bool ret = FALSE;     char *p_gga = in_data;          if (NULL == p_gga)     {         return ret;     }          if (NULL != (p_gga = strstr(p_gga, "$GNGGA")))     {         printf("gga data : %s ", p_gga);                  /* 数据校验 */         if (TRUE == data_check(p_gga))         {             printf("gga data check success! ");                          /* 解析出字符串 */             printf("gga data parse:  ");             for (int i = 0; i < GGA_STR_MAX; i++)             {                 sscanf(p_gga, "%[^,]", gps_gga_str[i]);                 printf("%s ", gps_gga_str[i]);                 p_gga = p_gga + (strlen(gps_gga_str[i]) + 1);             }                          /* 字符串转数字 */             out_data->latitude = atof(gps_gga_str[STR_LATITUDE]);             out_data->longitude = atof(gps_gga_str[STR_LONGITUDE]);             out_data->time = atof(gps_gga_str[STR_TIME]);             out_data->quality = atof(gps_gga_str[STR_QUALITY]);             ret = TRUE;         }         else         {             printf("gga data check error! ");         }     }          return ret; }
  这里使用 sscanf+正则表达式 来做解析。sscanf(p_gga, "%[^,]", gps_gga_str[i]);
  sscanf函数在做字符串相关解析时很好用,这里配合正则表达式来使用,上面这一句代码的意思就是从p_gga中取逗号前面的数据存放到gps_gga_str[i]中,因为gga数据都是用逗号隔开的,循环几次就可以把所有数据解析出来,很方便。
  正则表达式学习资源如: 1、https://deerchao.cn/tutorials/regex/regex.htm 2、https://www.runoob.com/regexp/regexp-syntax.html
  下面再看一下, sscanf+正则表达式 的几种简单用法:
  1、取指定长度的字符串。
  如在下例中,取最大长度为 4 字节的字符串。 sscanf("123456 ", "%4s", str);
  2、 取到指定字符为止的字符串。
  如在下例中,取遇到空格为止字符串。 sscanf("123456 abcdedf", "%[^ ]", str);
  3、取仅包含指定字符集的字符串。
  如在下例中,取仅包含 1 到 9 和小写字母的字符串。 sscanf("123456abcdedfBCDEF", "%[1-9a-z]", str);
  4、取到指定字符集为止的字符串。
  如在下例中,取遇到大写字母为止的字符串。 scanf("123456abcdedfBCDEF", "%[^A-Z]", str);
  sscanf+简单、易理解的正则表达式 的方法有时候可以帮助我们很方便地进行字符串数据地解析。sscanf+复杂的正则表达式 不太建议使用,因为代码可读性太差了。
  另外,使用 sscanf+正则表达式 时有必要写点注释,有见过这种方式还好,有些后面看你代码的人可能没接触过正则表达式可能一时半会儿理解不了。我之前大三出去实习的时候,在公司里就看到这样的代码,那时候知识储备还不够,第一次看到sscanf+正则表达式 这种解析方法,但是搜索又搜索不到相关答案,很苦恼。所以,平时有必要写一些注释,利人利己。
  参考:
  1、正点原子《ATK-NEO-6M GPS模块》资料。
  2、https://blog.csdn.net/absurd/article/details/1177092
  猜你喜欢:
  STM32如何收发float类型数据?
  嵌入式中自定义协议的一些典型例子
  1024G 嵌入式资源大放送!包括但不限于C/C++、单片机、Linux等。私信回复1024,即可免费获取!

当代成年人的一次性的生活当代成年人看似人间清醒,其实也有冲动的一面。很多事情都是一次性,劲头只停留在第一次,之后就是无尽的后悔反省。今天就带大家走进当代成年人的一次性生活。最后你或许经历过每次买完东西,身有些人就是不回头生性高傲的三大星座,一旦翻脸,就别想重归于好,再爱也不会水瓶座水瓶座无论对一个人好还是失望都是拿时间去换的,爱的时候会循序渐进,对你无情也是慢慢积攒了失望才会这样。在感情里,但凡水我在他乡挺好的,你呢?我有时候觉得,生活好像在故意为难我。上周北京大雨,公司让在家办公,但是偏巧小区旁边的酒店在装修,工人师傅叮叮哐哐的声音吵得我一个字也写不出来。更令人生气的是,平时好好的水管,突然爆多种形态,切换惬意,雷柏i100蓝牙TWS耳机,支持单耳双耳使用忙碌的一周还会开始,开不完的会,做不完的工作,脑负荷超标,只想一头钻入音乐的海洋。在盛夏的欢愉中,它有一份独特的吸引力。是一剂舒心良方,能坠入仲夏夜的梦中,温暖灵动美妙,全身心的沉雷柏mini亮相,与墨齐香,V86061cherry轴游戏机械键盘黑云翻墨未遮山白云跳珠乱入船欲制胜,先换装给大家介绍一位新朋友会耍萌,更加凶猛雷柏首款61键CherryMX轴机械键盘V8606160配列稳定输出收紧视线焦点精简之物象灵巧外表下蕴快充!缩小体积!雷柏首款氮化镓充电器PA65上市如何hold住大场面雷柏首款氮化镓充电器PA65做了示范个头小充电快省时间好多人问到底有多快?我说这么快!PA65氮化镓快充充电器采用第三代半导体材料氮化镓黑科技USBAUSBC双可变形的手感利器,雷柏V330幻彩RGB游戏鼠标加持犀利攻势雷柏推出了V330电竞游戏鼠标,可拓展侧裙大大提升了鼠标的可玩性和适应性,不论哪种握法习惯的玩家,都能在V330上收获美好手感。除此之外,V330的配置也诚意满满,PMW3327游周深为大乔孙策献上微光海洋,雷柏i130TWS耳机,听水晶峡谷新一代的青春不仅仅再是磁带CD,在谈天论地时还夹带着陪伴攻打水晶的峡谷也是相当浪漫。5V5王者峡谷中的故事,有过纷争,也有彼此间细腻的情感。感情升华的开始每个英雄也慢慢拥有了他们独雷柏新女性系列ralemo,乐萌赋予自信之美,观感和触感的能量我们会不断与新的生活相遇,会有心情独白,会为往后留白,在解惑路上如何收获简单而巧妙地快乐?今天,雷柏开启了一个名叫萌发趣味的项目,探索女性在不同生命历程的感知之美。能唤起情感的共鸣雷柏新女性系列ralemo发布,乐萌秘境,打造更高级的感官型产品初见,是表面云淡风轻,内心却溃不成军,hi,ralemo。我们会不断与新的生活相遇,会有心情独白,会为往后留白,在解惑路上如何收获简单而巧妙地快乐?老牌实力大厂,外设领军品牌雷柏,为SurfacePro定制,蓝牙5。0USBC充电,雷柏XK200蓝牙键盘升级延续操作的减负与安装的贴合触发新的生产创造力通过chfa全新升级的能效攻势,升级蓝牙5。0升级USBC充电口因接而感,因触而动用便捷的力量让原本的纤薄智控更加自如表达硬核装备,高效
数码博主的小米11Ultra真实体验五年老果粉都被打动了先说说外观,小米11Ultra的正面视觉观非常精美,使用的是一块6。81英寸挖孔四曲面全面屏,分辨率达到了32001400WQHD,也就是我们常说的2K分辨率,刷新率提高到了120小米新专利曝光,机身无实体按键和充电接口小米的手机专利是真的非常多,不管是手机外观设计还是摄像模组的专利,都可以说是搞得像模像样。近期相关消息显示,小米又有新的一项手机专利曝光,虽然手机的整体和目前市面上的手机大体设计相比亚迪K9在中国卖两百多万人民币为什么在日本和英国要卖到四百万?国内只卖200万,但在国外一律卖400多万的比亚迪K9堪称中国制造的骄傲,据悉,K9已经卖给了欧洲十几个国家几十个城市伦敦巴黎不来梅波恩马德里巴塞罗那萨尔茨堡华沙阿姆斯特丹布鲁塞尔好消息中国邮政要全面提速了收快递更快更安全中国邮政要全面提速了,从这个月开始,全国一千多个城市今天邮件儿第二天你就能收到货了。说起邮政国,大家第一印象应该是稳定覆盖范围广和安全。很有意思啊,中国所有大学这个录取通知书都是通我的快递到底是谁在做主?你是如何看待快递不送货上门的?电商行业发展迅猛,大家都已经习惯于在网上开启各种买买买的模式。可是网购完之后,很多朋友就开始发懵了,为什么自己的快递从来没有送上门过,每次都是直接送到快递驿站,让自提呢?我的快递到凭借这五种空调的省电用法,我家上个月的电费,比邻居家少一半都说夏天的命是空调给的,但是看一看空调的电费清单,恐怕半条命都要被吓没了。上个月我家邻居拿着四百多元的电费清单来找我抱怨,我在安慰她的同时,顺便展示了一下自己家不到二百元的电费清单1000元以下销量最好的手机是哪款?手机买新不买旧!当下1000元以下销量最好的手机魅蓝S61月17日,魅蓝S6发布1月19日首发当天,成各大平台单品销量冠军作为魅族2018年的开山作品,虽然魅蓝S6定价不足千元,但特斯拉6月销量3。3万!说实话,维权事件对特斯拉真没影响众所周知,在4月末的上海车展上,安阳车主张女士站上特斯拉车顶维权,引发了整个车圈大震动。自那时候起,很多网友就一直在猜测,维权事件对特斯拉而言,究竟会有什么样的影响,会不会引发销量明年新iPhone将有多项变化,新款MacBookAir有多种颜色选择你们好,这里是Seek思科,为你寻找苹果最新资讯苹果隐藏技巧苹果良心APP以及苹果优质配件。如果你需要这些,不妨点个关注。距离发布苹果iPhone12系列已经过去将近9个月,而新一银行大力围堵虚拟币交易,大范围精准识别仍然吃力有六大行人士表示,已有大行对境外炒币账户转账进行严格控制。这意味着,大银行内部对炒币账户已有识别。日前,上海一位接近六大行的人士表示,人民银行要求各个银行机构建立反洗钱系统。如果有各位大佬,请问当年的华为P20相机是不是和现在的差很多?我也还用着,电池坏了,收购换了一块电池。64g的,内存有点不够了。2018入手的p20手机,我还在用,很好!值得拥有。我还在用,觉得没什么不好的!对了,换了块电池。华为P20手机的