Qt开发北斗定位系统融合百度地图API及Qt程序打包发布

1、上位机介绍

最近有个接了一个小型项目,内容很简单,就是解析北斗GPS的串口数据然后输出经纬度,但接过来觉得太简单,就发挥了主观能动性,增加了百度地图API,不但能实时定位,还能在地图上标识出位置信息,用的QT5.5。上位机运行图片如图所示:整体运行比较流畅。

原理就是界面上集成一个WebKits/WebView,让Qt和Javascript进行交互。但需要注意Qt5.6以上版本取消了WebView的模块,换成了webenginewidgets,看上去配置好麻烦,甚至还要自己编译什么的,虽然性能可能有指数性的提升,但对于我这个做嵌入式软件和硬件,上位机会个基础的就算是很好的人来说,还是webkits好一点。

2017/07/22 更新:


在本文后续版本已经适配了Qt 5.6 以上版本的QWebEngine版本,摒弃了QWebKits组件,后续若有经费的话,将继续更新支持QWebChannel通信通道。两个版本都可以在本文尾部的附件中下载,欢迎学习讨论。


2. 开发介绍

本设计开发主要涉及三个方面:

  • 串口开发(北斗GPS基于UART的,波特率115200,8,1),这个北斗GPS模块隔1s发一次GPS数据组,会通信几个卫星接收数据,时而一些卫星不反馈数据。
  • 数据解析。数据解析模块包括把几个卫星的数据按协议分开然后解析出来,这里有个难点在于Qt串口和CH340缓存BUG导致的数据包粘连和数据不连续解决。
  • 地图API驱动

2.1 串口开发

串口开发不用说了,请参考我前几篇有个蓝牙的博客,上面有源码,Qt on Android 蓝牙开发,本设计中的串口部分就是基于那个串口开发的。串口开发自动检测连接设备,不需要进入管理器和找到COM口是多少,自动和串口进行连接。

2.2 数据解析

串口数据粘包和数据不连续很头疼,进入一个串口接收槽函数QString rxArray.append(serialPort->readAll() ); 接收数据不完整,或者说会分好几次进行接收,而且分好几次接收长度没有规律,所以无法直接使用接收的数据。

1S钟GPS发送一次数据为:

/*
$GNRMC,114821.880,V,3957.378130,N,11620.848015,E,0.000,0.000,230417,,E,N*23
$GNGGA,114821.880,3957.378130,N,11620.848015,E,0,00,127.000,100.800,M,0,M,,*6D
$GNGLL,3957.378130,N,11620.848015,E,114821.880,V,N*52
$GNGSA,A,1,,,,,,,,,,,,,127.000,127.000,127.000*2A
$GNGSA,A,1,,,,,,,,,,,,,127.000,127.000,127.000*2A
$GPGSV,1,1,4,17,57,315,21,22,35,67,,28,75,176,,30,12,204,*74
*/

数据量比较巨大,所以这里增加处理机制,尽量保存完整数据。

// 接收数据槽函数

void Widget::RxData(){

    QString rxString;

    rxArray.append(serialPort->readAll());
//qDebug() << QString(rxArray);
if( serialRead == true ){
// 数据对齐,如果上次数据是一半,抛弃数据,重新接受
times++;
rxString = QString(rxArray);
//qDebug() << "rec:" << rxString;
ui->textBrowser->append(tr("--------------------------------------------------------------------------"));
ui->textBrowser->append("从北斗GPS传感器第("+QString::number(times)+")次接受数据:");
ui->textBrowser->append(tr("--------------------------------------------------------------------------"));
ui->textBrowser->append(QString(rxArray));
gpsDatasProcessing( rxArray );
rxArray.clear();
serialRead = false;
if( times%50 == 0 ) {
ui->textBrowser->clear();
}
ui->textBrowser->append(tr("--------------------------------------------------------------------------\r"));
}else{
return;
}
// 解析数据 }
void Widget::gpsDatasProcessing(QByteArray GPSBuffer)
{ QString GPSBufferString = QString( GPSBuffer );
int error_pos = 0;
QString GNRMC_String = NULL;
QString GPGGA_String = NULL;
QString GPGSV_String = NULL;
QString GPRMC_String = NULL;
QString GPGLL_String = NULL;
QString GNGGA_String = NULL;
bool latiflag = false;
bool atiflag = false;
bool utcflag = false;
bool speedflag = false;
bool longtiflag = false; QList<QString> gpsStringList = GPSBufferString.split('\n'); // 由于定时间隔,数据包发生黏连,纠正数据。
if( gpsStringList.at(0).at(0) != '$' ) {
QString ErrorString = gpsStringList.at(gpsStringList.length()-1) + gpsStringList.at(0);
error_pos = 1;
if( ErrorString.contains("$GNRMC") ){
GNRMC_String = ErrorString;
}else if( ErrorString.contains("$GPGGA") ) {
GPGGA_String = ErrorString;
}else if( ErrorString.contains("$GPGSV") ) {
GPGSV_String = ErrorString;
}else if( ErrorString.contains("$GPRMC") ) {
GPRMC_String = ErrorString;
}else if( ErrorString.contains("$GPGLL") ) {
GPGLL_String = ErrorString;
}else if( ErrorString.contains("$GNGGA") ) {
GNGGA_String = ErrorString;
} }else{
error_pos = 0;
}
// 从QList中得到数据
for( int i = error_pos; i < gpsStringList.length()- error_pos; i++ ) {
if( gpsStringList.at(i).contains("$GNRMC") ){
GNRMC_String = gpsStringList.at(i);
}else if( gpsStringList.at(i).contains("$GPGGA") ) {
GPGGA_String = gpsStringList.at(i);
}else if( gpsStringList.at(i).contains("$GPGSV") ) {
GPGSV_String = gpsStringList.at(i);
}else if( gpsStringList.at(i).contains("$GPRMC") ) {
GPRMC_String = gpsStringList.at(i);
}else if( gpsStringList.at(i).contains("$GPGLL") ) {
GPGLL_String = gpsStringList.at(i);
}else if( gpsStringList.at(i).contains("$GNGGA") ) {
GNGGA_String = gpsStringList.at(i);
}
}
if( !GPGGA_String.isNull() ) {
QList<QString> gpggaStrList = GPGGA_String.split(",");
QString utcstr = gpggaStrList.at(1);
ui->lineEdit_UTC->setText("格林威治时间:"+utcstr.mid(0,2)+":"+utcstr.mid(2,2)+":"+utcstr.mid(4,2));
QString latistr = gpggaStrList.at(2);
ui->lineEdit_latitude->setText("北纬"+latistr.mid(0,2)+"度"+latistr.mid(2,7)+"分");
QString altistr = gpggaStrList.at(4);
ui->lineEdit_longitude->setText("西经"+altistr.mid(0,3)+"度"+altistr.mid(3,7)+"分");
utcflag = true;
latiflag = true;
atiflag = true;
}
if( !GNGGA_String.isNull() ) {
if( !latiflag ) {
QList<QString> gnggaStrList = GNGGA_String.split(",");
QString utcstr = gnggaStrList.at(1);
UTC2BTC(&utcstr);
ui->lineEdit_UTC->setText("北京时间:"+utcstr.mid(0,2)+":"+utcstr.mid(2,2)+":"+utcstr.mid(4,2));
QString latistr = gnggaStrList.at(2);
ui->lineEdit_latitude->setText("北纬"+latistr.mid(0,2)+"°"+latistr.mid(2,9)+"'");
double double_lati = latistr.mid(0,2).toDouble()+(latistr.mid(2,7).toDouble()+0.25)/60;
QString altistr = gnggaStrList.at(4);
ui->lineEdit_longitude->setText("西经"+altistr.mid(0,3)+"°"+altistr.mid(3,9)+"'");
double double_alti = altistr.mid(0,3).toDouble()+(altistr.mid(3,7).toDouble()+0.25)/60; setCoordinate(QString::number(double_alti),QString::number(double_lati));
//setCoordinate(QString::number(108.886119),QString::number(34.223921));
qDebug()<< "纬度:"<<QString::number(double_alti)<<"|"<<"经度:"<< QString::number(double_lati) << "\n";
QString longtistr = gnggaStrList.at(9);
ui->lineEdit_altitude->setText(longtistr+"m ");
ui->lineEdit_speed->setText("无效PPS"); utcflag = true;
latiflag = true;
atiflag = true; }
} }
void Widget::slotSerialTimerOut()
{
if( serialRead == false ){
serialRead = true;
}
}

2.3 百度地图API

百度地图API我找了很多资料,参考资料本文附录,非常感谢博客名“灿哥哥”,“我是大坏蛋”的整理,“灿哥哥”在文章中不但提供了离线地图和方法,还提供了对于地图的基本介绍,对于地图上面的操作,请参考灿哥哥的博客。但就我开发我想提出两点:

  • 本设计使用的是离线地图,地图包30M左右,不联网也可以使用。把地图包,放在编译的release或者debug文件夹下,载Qt主程序中的url协商地图html的位置。
  • 如果使用的是在线地图就需要连接网络,最重要的是坐标转换,经纬度需要和百度地图坐标进行一个转换,这个转换是通过百度地图API的接口进行的,而且普通用户转换还有次数限制。  
  • 这个离线地图不需要转换,直接使用经纬度就可以定位。

下面代码可以看到Qt和Javascript如何互动的。

void Widget::getCoordinate(QString lon,QString lat)
{
QString tempLon="鼠标经度:"+lon+"°";
QString tempLat="鼠标纬度:"+lat+"°";
ui->labelMouseLongitude->setText(tempLon);
ui->labelMouseLatitude->setText(tempLat);
} void Widget::setCoordinate(QString lon,QString lat)
{
QWebFrame *webFrame = ui->webView->page()->mainFrame();
QString cmd = QString("showAddress(\"%1\",\"%2\")").arg(lon).arg(lat);
webFrame->evaluateJavaScript(cmd);
}
void Widget::on_pushButtonStreetMap_clicked()
{
QWebFrame *frame = ui->webView->page()->mainFrame();
QString cmd = QString("showStreetMap()");
frame->evaluateJavaScript(cmd);
ui->pushButtonSatelliteMap->setEnabled(true);
ui->pushButtonStreetMap->setEnabled(false);
} void Widget::on_pushButtonSatelliteMap_clicked()
{
QWebFrame *frame = ui->webView->page()->mainFrame();
QString cmd = QString("showSatelliteMap()");
frame->evaluateJavaScript(cmd);
ui->pushButtonSatelliteMap->setEnabled(false);
ui->pushButtonStreetMap->setEnabled(true);
} void Widget::slotPopulateJavaScriptWindowObject()
{
ui->webView->page()->mainFrame()->addToJavaScriptWindowObject("ReinforcePC", this); }

3. 程序打包

做完这个程序之后呢,我开始研究如何给程序打包,终于在两个小时内搞定了。

Step1:把所需要的dll文件集成出来。

Step2:用工具把dll文件exe文件和其他文件封装起来,做成msi或者exe文件。

工具就是一个Qt自带的windeployqt工具,另一个是Advanced installer安装包打包程序。

3.1 搜集dll文件

1)在开始菜单找到Qt文件夹,里面有个像是cmd命令行一样的东西,我的是MinGW的。反正运行出来这个样子:

2)进入Qt的工程文件夹,在release或者debug里面(看你用的是release编译还是debug编译了),找到生成的exe文件夹,把这个exe文件复制到一个方便找,方便输入路径的地方。我放在了D:\setup文件里了。

3)在刚才出那个命令行里面输入: cd /d D:\setup 切换到这个文件夹。

4)输入命令: windeployqt xxx.exe xxx.exe就是你刚才编译出exe的名字。

然后你会发现Qt把所有这个exe文件需要的.dll文件和其他支持库文件都放在这个文件夹了。实际上到了这步你可以打成压缩包然后发布到任何电脑,解压直接运行

3.2 使用Advanced Installer打包程序

我还是比较喜欢把他弄成安装包,这样更方便,更正式。Advanced Installer这个工具简直太赞了,打包的程序安装界面十分的正式,一点都不山寨,还有很多安装包的皮肤可供选择。如图为打好包的图标:

运行后的效果如图:

里面还提供了导入注册表、安装后创建快捷方式等等方便的配置。

** 下载地址:http://down7.pc6.com/gm1/Advanced Installer.zip **

使用教程,还是参考后面的参考文献中的【4】,这里不在赘述了。

本文附件:

[1] 本程序旧版安装包下载地址如下百度云盘地址 提取码:41hx (2017-04-29)

[2] 本程序新版安装包下载地址如下:百度云盘地址 提取码:o59r (2017-07-22更新)

参考文献:

[1] 我是大坏蛋,gps定位Qt界面百度地图api的介绍,CSDN博客,2014-08-24

[2] 灿哥哥,Qt加载百度离线地图,CSDN博客,2016-03-30

[3] winland0704,Qt官方开发环境生成的exe发布方式--使用windeployqt,Qt百度贴吧,2015-04-28

[4] Prodesire,Windows安装包制作指南-Advanced Installer的使用,cnBlogs博客,2016-08-18

[5] jwq2011的专栏,GPS数据包格式+数据解析,CSDN博客,2016-12-15

版权声明:

1· 本文为MULTIBEANS团队研发跟随文章,未经允许不得转载。

2· 文中涉及的内容若有侵权行为,请与本人联系,本人会及时删除。

3· 尊重成果,本文将用的参考文献全部给出,向无私的工程师,爱好者致敬。


Qt开发北斗定位系统融合百度地图API及Qt程序打包发布的更多相关文章

  1. ***微信LBS地理位置开发+百度地图API(地理位置和坐标转换)

    微信公众平台开发 - 获取用户地理位置 本文介绍在微信公众平台上如何使用高级接口开发获取用户地理位置的功能. 一.获取用户地理位置接口 开通了上报地理位置接口的公众号,用户在关注后进入公众号会话时,会 ...

  2. 百度地图API开发指南

    简介什么是百度地图API? 百度地图API是一套由JavaScript语言编写的应用程序接口,它能够帮助您在网站中构建功能丰富.交互性强的地图应用.百度地图API包含了构建地图基本功能的各种接口,提供 ...

  3. 深入浅出百度地图API开发系列(1):前言

    百度地图API目前在地图API领域越来越受到众多开发者的关注,许多应用都使用到了百度地图API服务,包括博主me,我自己使用做的是Javascript API,根据经验,我想整理出一份系列教程,如果能 ...

  4. 深入浅出百度地图API开发系列(3):模块化设计

    在前面两张简单介绍了百度地图API的基础知识和使用之后,我们来分析一下百度地图API的基本架构,了解一下基本架构可以帮助我们更清晰的了解API的功能和调用过程,也就可以帮助我们在实际开发中可以更方便的 ...

  5. 深入浅出百度地图API开发系列(2):创建地图

    上一篇文章里,先介绍了一下百度地图API开发所涉及到的一些基础概念,包括投影,坐标系等基础概念,再有了这些基础后,我们可以开始开发自己的web地图了.先来个代码示例(建议大家都是用百度地图API大众版 ...

  6. Android 百度地图API(01)_开发环境 HelloBaiduMap

    转载于:http://blog.csdn.net/lmj623565791/article/details/37729091 转载于:http://blog.csdn.net/crazy1235/ar ...

  7. 基于MFC与第三方类CWebPage的百度地图API开发范例

    在进行百度地图API开发之前你需要到http://developer.baidu.com/map申请密匙 密匙申请之后就可以进行百度地图API的开发了. 下面我们以在visual c++6.0里进行地 ...

  8. 百度地图API开发一——仿照现有测距效果实现测面功能

      最近做了一个百度地图API的开发项目,里面有测量距离和测量面积的功能需求.测量距离百度给出了封装好的javascript包——BMapLib.DistanceTool,效果如下: 这个效果体验很好 ...

  9. 使用百度地图API进行Android地图应用开发(Eclipse)

    随着基于位置的服务的兴起,地图类App呈现爆发趋势.随着而来的是地图供应商开放大量的API.供开发人员开发基于PC或者移动端的应用程序. 如今我们研究使用百度地图SDK进行Android项目的开发. ...

随机推荐

  1. CF1101B Accordion 模拟

    前后扫一遍: #include<iostream> #include<cstdio> #include<algorithm> #include<cstdlib ...

  2. Session_Start

    第一次启动浏览器后,执行了包含Session_Start的方法时触发,当关闭浏览器或者SESSION超时后,自动销毁.

  3. JavaScript Succinctly 读后笔记

    1.JavaScript does not have block scope  2.Scope is determined during function definintion,  not invo ...

  4. Apache 403 错误。。

    两个方面.. 一: httpd.conf  是否有 <directory '/www'></directory> 是否有  Deny from all 或者 Require l ...

  5. light oj 1047 - Neighbor House(贪心)

    The people of Mohammadpur have decided to paint each of their houses red, green, or blue. They've al ...

  6. Lonsdor K518ISE Key Programmer Review

    Lonsdor K518ISE key programmer is the latest version of Lonsdor, with wider vehicle coverage in key ...

  7. SpringBoot初始教程之Servlet、Filter、Listener配置

    1.介绍通过之前的文章来看,SpringBoot涵盖了很多配置,但是往往一些配置是采用原生的Servlet进行的,但是在SpringBoot中不需要配置web.xml的 因为有可能打包之后是一个jar ...

  8. Storm与Spark区别

    Storm擅长于动态处理大量实时生产的小数据块,概念上是将小数据量的数据源源不断传给过程: Spark擅长对现有的数据全集做处理,概念是将过程传给大数据量的数据. 二者设计思路相反.Storm侧重于处 ...

  9. C# 控制反转(IOC: Inverse Of Control) & 依赖注入(DI: Independence Inject)

    举例:在每天的日常生活中,我们离不开水,电,气.在城市化之前,我们每家每户需要自己去搞定这些东西:自己挖水井取水,自己点煤油灯照明,自己上山砍柴做饭.而城市化之后,人们从这些琐事中解放了出来,城市中出 ...

  10. 配置编译器(GCC和GFortran)

    平台信息 Description: CentOS Linux release 7.6.1810 (Core) 检查环境 $ gfortran -v $ gcc -v 安装 GCC和Fortran 环境 ...