基于EC20 CE FILG GPS数据采集的总结

it2023-03-25  82

     模块采用 Quectel  EC20 CE FILG (4G+GPS),这里我只对软件部分实现GPS数据的采集、数据帧解析和GPS漂移点过滤的一些方法和要点进行总结。首先,上效果图:

需求: 采集GPS位置信息,定时上传GPS数据到平台(平台上展示gps锚点和轨迹信息部分,不再本文讨论范围)

1、GPS数据采集前的准备工作

    GPS设置,可以通过调用AT指令,依次执行以下操作,

"AT+QGPSCFG=\"outport\",\"usbnmea\"" "AT+QGPS=1" "AT+QGPS?" "AT+QGPSLOC=?"

笔者本地使用microcom工具,指令如下:

// 通过管道执行shell指令并返回结果 static bool executeCMD(const char *cmd, char *result) { char buf_ps[2048]; char ps[1024]={0}; FILE *ptr = NULL; strcpy(ps, cmd); ptr=popen(ps, "r"); memset(result,0,strlen(result)); if(ptr!=NULL) { while(fgets(buf_ps, 1024, ptr)!=NULL) { strcat(result, buf_ps); if(strlen(result)>1024) break; } pclose(ptr); ptr = NULL; c_debug("cmd %s exec result is :\n%s",cmd,result); return true; } else { c_debug("thread cmd open failed"); return false; } } // 初始化GPS模块 void initGPS(char* cmd){ char buff[1024] = {0}; sprintf(buff,"echo -e \"%s\\r\\n\" | microcom -s 115200 -t 2000 /dev/ttyUSB2",cmd); executeCMD(buff,result); }

另外说明, EC20 CE FILG 模块包含4个驱动,/dev/ttyUSB2 串口用于接收AT指令并输出执行结果

在执行初始化GPS操作前,可以监听 /dev/ttyUSB2 口

cat /dev/ttyUSB2

另外,我们查询4G模块相关详细,如

“AT+CSQ”  ==》 查询信号强度

“AT+CIMI” ==》 SIM卡检测 

...

等等,相关执行结果都可以在 /dev/ttyUSB2 口查看到

2、查看gps数据

gps数据查看有以下两种方式:

(1)使用AT指令查看:

AT+QGPSLOC= 2 ; 备注:查询到的经纬度格式 format: (-)dd.ddddd,(-)ddd.dddd ,即以度为单位 除了2 之位,还有其他的参数可设置,具体,可查看模块操作相关说明文档

(2)通过监听 /dev/ttyUSB1 口可以查看,可以看到有大量的GPS数据从此端口吐出(output)

cat /dev/ttyUSB1

 这里,我们如果需要编写程序从/dev/ttyUSB1 口循环读取gps数据,可以参考Linux下C语言串口编程通讯示例,这里不再过多介绍。 

这里有个的坑需要注意:默认输出的经纬度信息 GPGGA中,使用格式 dddmm.mmmmmm ,一定要进行响应的转换

3、经纬度转换

   采集的原数据为WGS-84格式,如果我们使用百度地图的API,需要转换成BD09,这里提供一套网上的转换方法,项目测试可用。

// .h struct gps_position_t { int32_t lat; /*< Latitude, expressed as degrees * 1E7*/ int32_t lon; /*< Longitude, expressed as degrees * 1E7*/ double lat_f; double lon_f; }; class QGCTransform { public: QGCTransform(); /*WGS-84 to GCJ-02*/ //double PI; double a; double ee; gps_position_t gps_t; void Transform(); void Transform(double lat, double lon); void bd_encrypt(double gg_lat, double gg_lon, double &bd_lat, double &bd_lon); void bd_decrypt(double bd_lat, double bd_lon, double &gg_lat, double &gg_lon); private: bool OutOfChina(double lat, double lon); double TransformLat(double x, double y); double TransformLon(double x, double y); }; // .cpp #define PI (3.14159265358979324) //其中 bd_encrypt 将 GCJ-02 坐标转换成 BD-09 坐标, bd_decrypt 反之 const double x_pi = 3.14159265358979324 * 3000.0 / 180.0; void QGCTransform::bd_encrypt(double gg_lat, double gg_lon, double &bd_lat, double &bd_lon) { double x = gg_lon, y = gg_lat; double z = sqrt(x * x + y * y) + 0.00002 * sin(y * x_pi); double theta = atan2(y, x) + 0.000003 * cos(x * x_pi); bd_lon = z * cos(theta) + 0.0065; bd_lat = z * sin(theta) + 0.006; } void QGCTransform::bd_decrypt(double bd_lat, double bd_lon, double &gg_lat, double &gg_lon) { double x = bd_lon - 0.0065, y = bd_lat - 0.006; double z = sqrt(x * x + y * y) - 0.00002 * sin(y * x_pi); double theta = atan2(y, x) - 0.000003 * cos(x * x_pi); gg_lon = z * cos(theta); gg_lat = z * sin(theta); } QGCTransform::QGCTransform(){ a = 6378245.0; ee = 0.00669342162296594323; } /*WGS-84 to GCJ-02*/ bool QGCTransform::OutOfChina(double lat, double lon){ if (lon < 72.004 || lon > 137.8347) return true; if (lat < 0.8293 || lat > 55.8271) return true; return false; } double QGCTransform::TransformLat(double x, double y){ double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * qSqrt(qAbs(x)); ret += (20.0 * qSin(6.0 * x * PI) + 20.0 * qSin(2.0 * x * PI)) * 2.0 / 3.0; ret += (20.0 * qSin(y * PI) + 40.0 * qSin(y / 3.0 * PI)) * 2.0 / 3.0; ret += (160.0 * qSin(y / 12.0 * PI) + 320 * qSin(y * PI / 30.0)) * 2.0 / 3.0; return ret; } double QGCTransform::TransformLon(double x, double y) { double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * qSqrt(qAbs(x)); ret += (20.0 * qSin(6.0 * x * PI) + 20.0 * qSin(2.0 * x * PI)) * 2.0 / 3.0; ret += (20.0 * qSin(x * PI) + 40.0 * qSin(x / 3.0 * PI)) * 2.0 / 3.0; ret += (150.0 * qSin(x / 12.0 * PI) + 300.0 * qSin(x / 30.0 * PI)) * 2.0 / 3.0; return ret; } void QGCTransform::Transform(){ double lat_s=gps_t.lat * 1E-7; double lon_s=gps_t.lon * 1E-7; Transform(lat_s, lon_s); } void QGCTransform::Transform(double lat_s, double lon_s){ gps_t.lat_f = lat_s; gps_t.lon_f = lon_s; gps_t.lat = lat_s * 1E7; gps_t.lon = lon_s * 1E7; if (OutOfChina(lat_s, lon_s )) { return; } double dLat = TransformLat(lon_s - 105.0, lat_s - 35.0); double dLon = TransformLon(lon_s - 105.0, lat_s - 35.0); double radLat = lat_s / 180.0 * PI; double magic = qSin(radLat); magic = 1 - ee * magic * magic; double sqrtMagic = qSqrt(magic); dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * PI); dLon = (dLon * 180.0) / (a / sqrtMagic * qCos(radLat) * PI); gps_t.lat = (lat_s + dLat) * 1E7; gps_t.lon = (lon_s + dLon) * 1E7; gps_t.lat_f = lat_s + dLat; gps_t.lon_f = lon_s + dLon; }

 

4、漂移点过滤

   关于漂移点过滤的逻辑,我们可以简单使用如下方法处理,大致提升采集数据的可靠性(经度更多依赖于硬件本身,这里我们只讨论软件过滤辅助提高经度的方法)

(1)过滤无效点。 $GPRMC <2>状态, A 为有效位置, V为非有效接收警告

(2)采集样本点。前10个点不上传,用于判断设备的运动模式,如果超过6个以上采集到速度为0,视为静止,否则可以视为运动模式处理。

(3)静止模式下,前10个点有两中处理方式,第一种直接将最后一个有效点作为基准点处理;第二种,计算10个样本点的离散中心,作为基准点;

(4)运动状态下,则采用下几个步骤进一步过滤,然后以最后一个有效点作为运动状态的基准点上传数据;          采用大速度过滤原则:通过GPVTG获取地面速度,如果速度大于130Km/h,则过滤          采用大距离过滤原则:通过计算前后采集相邻两采集点之际的距离,如果距离超过50m则过滤(可调整)          采用大加速度过滤原则:目前一般轿车10秒内可从零加速到100km/h,平均加速度为2.778m/s^2,即大约0.28g,最大加速度可达到0.6g左右。这里通过计算前后gps位置点的加速度,如果超过0.6g,则过滤掉。

另,具体开发遇到坑比较多,祝好运🤞  

参考:一篇关于GPS定位写得最详实清晰的文章之一

 

最新回复(0)