GPS模块运用: 定位测试软件设计(上位机)

一、硬件介绍

GPS模块型号:  中科微电子ATGM336H-5N 系列模块

ATGM336H-5N 系列模块是 9.7X10.1 尺寸的高性能 BDS/GNSS 全星座定位导航模块系列的总称。该系列模块产品都是基于中科微第四代低功耗 GNSS SOC 单芯片—AT6558,支持多种卫星导航系统,包括中国的 BDS(北斗卫星导航系统),美国的 GPS,俄罗斯的 GLONASS,欧盟的 GALILEO,日本的 QZSS以及卫星增强系统 SBAS(WAAS,EGNOS,GAGAN,MSAS)。AT6558 是一款真正意义的六合一多模卫星导航定位芯片,包含 32 个跟踪通道,可以同时接收六个卫星导航系统的 GNSS 信号,并且实现联合定位、导航与授时。

二、软件功能介绍

解析GPS模块接收的数据,得到经纬度和速度。

软件支持GPS模式配置、指定的语句输出配置。

软件设计思路: 

串口在子线程里完成数据接收,将解析的数据保存到全局类中,全局类里加了读写锁,防止多线程读写全局变量出现问题。

主UI线程里负责数据显示,解析的结果使用定时器2秒钟显示一次。

软件下载地址: https://download.csdn.net/download/xiaolong1126626497/12721684

软件运行效果:

GPS模块连线:

三、软件核心代码

3.1 uart_code.h

#ifndef UART_CODE_H
#define UART_CODE_H
#include <QSerialPort>
#include <QObject>
#include <QThread>
#include <QDebug>
#include <QString>
#include "config.h"
class OBD_Data
{
public:
      QString V; //电瓶电压
      QString R; //转速
      QString S; //速度
      QString P; //绝对气门开度
      QString O; //发动机负荷
      QString C; //冷却液温度
      QString L; //油量
      QString M; //此次行程米
      QString F; //此次油耗L
      QString XM; //瞬时油耗、百公里油耗
      QString T; //运行时间
      //QString E; //发动机状态 0熄火 1点火 2自动启动
      QString A; //急加速次数
      QString B; //急刹车次数
      QString D; //故障码个数
      QString GX;//X
      QString GY;//Y
      QString GZ;//Z
      QString data;
};

class GPS_Data
{
private:
    double lat; //纬度
    double lng; //经度
    QString speed; //速度
    QReadWriteLock lock; //读写锁
    QString save_src_data; //原始数据
public:
    QString Data;
    //构造函数
    GPS_Data()
    {
        lat=0.0;
        lng=0.0;
        speed="0";
        save_src_data="";
    }
    //读取纬度
    double get_lat(void)
    {
        lock.lockForRead();
        double tmp_lat=lat;
        lock.unlock();
        return  tmp_lat;
    }
    //设置纬度
    void set_lat(double tmp_lat)
    {
        lock.lockForWrite();
        lat=tmp_lat;
        lock.unlock();
    }
    //读取经度
    double get_lng(void)
    {
        lock.lockForRead();
        double tmp_lng=lng;
        lock.unlock();
        return  tmp_lng;
    }
    //设置经度
    void set_lng(double tmp_lng)
    {
        lock.lockForWrite();
        lng=tmp_lng;
        lock.unlock();
    }
    //读取速度
    QString get_speed(void)
    {
        lock.lockForRead();
        QString tmp_speed=speed;
        lock.unlock();
        return  tmp_speed;
    }
    //设置速度
    void set_speed(QString tmp_speed)
    {
        lock.lockForWrite();
        speed=tmp_speed;
        lock.unlock();
    }
    //读取原始数据
    QString get_src_data(void)
    {
        lock.lockForRead();
        QString tmp_speed=save_src_data;
        lock.unlock();
        return  tmp_speed;
    }
    //设置原始数据
    void set_src_data(QString tmp_save_src_data)
    {
        lock.lockForWrite();
        save_src_data+=tmp_save_src_data;
        if(save_src_data>1024)save_src_data.clear();
        lock.unlock();
    }
    //清除原始数据
    void set_src_clear(void)
    {
        lock.lockForWrite();
        save_src_data.clear();
        lock.unlock();
    }
};

class UART_ReadWriteThread:public QObject
{
    Q_OBJECT
public:
    QByteArray byte_all;
    UART_ReadWriteThread(QObject* parent=nullptr):QObject(parent){}
    ~UART_ReadWriteThread();
    //成员变量
    QSerialPort *UART_Config=nullptr; //串口---数据接收串口
    int GPS_GetData(const char *gps_src,const char *find_head,char *buff,int cnt);
    int GPS_GetCheckSum(const char *src_str,const char *find_str);
public slots:
    void UART_ReadUasrtData(void);
    void run();
    void Open_UART(void);
    void Close_UART(void);
    void UART_Send(QString text);
signals:
    void LogSend(QString text);
    void SendUartState(qint8 state);  //发送串口状态   true 表示连接 否则表示断开
};
#endif // UART_CODE_H

3.2 uart_code.cpp

#include "uart_code.h"
#include "config.h"

QString current_SerialPort="";  //当前串口端口号
qint32 current_SerialBaudRate=0; //当前波特率

class OBD_Data obd_data;
class GPS_Data gps_data;

UART_ReadWriteThread::~UART_ReadWriteThread()
{

}

void UART_ReadWriteThread::run()
{
    //串口配置
    if(UART_Config)
    {
        UART_Config->close();
        delete UART_Config;
        UART_Config=nullptr;
    }
    UART_Config =new QSerialPort;
    //打开串口设备
    Open_UART();
}


//当串口打不开的时候,间隔一段时间再次重新测试
void UART_ReadWriteThread::Open_UART(void)
{
    //配置串口属性
    UART_Config->setBaudRate(current_SerialBaudRate);              //默认波特率
    UART_Config->setDataBits(QSerialPort::Data8);  //数据位
    UART_Config->setParity(QSerialPort::NoParity); //奇偶校验
    UART_Config->setStopBits(QSerialPort::OneStop);//停止位
    UART_Config->setFlowControl(QSerialPort::NoFlowControl); //流控开关
    UART_Config->setPortName(current_SerialPort);    //COM的名称

    //1 数据传输串口连接 2 数据传输串口断开
    if(!(UART_Config->open(QIODevice::ReadWrite)))      //打开的属性权限
    {
        LogSend("数据传输串口打开失败.\n");
        SendUartState(2); //发送信号 通知状态改变
    }
    else
    {
        LogSend("数据传输串口打开成功.\n");
        SendUartState(1); //发送信号 通知状态改变
        //关联数据读信号
        connect(UART_Config,SIGNAL(readyRead()),this,SLOT(UART_ReadUasrtData()),Qt::QueuedConnection);
    }
}

//读取串口数据
void UART_ReadWriteThread::UART_ReadUasrtData()
{
    QString byte;
    /*读取串口缓冲区所有的数据*/
    byte=UART_Config->readAll();
    LogSend(byte);

    /*读取串口缓冲区所有的数据*/
    gps_data.Data+=byte;

    //完整的1帧VTG--RMC数据在109字节左右
    //if(gps_data.Data.size()>=200)
    if(gps_data.Data.count("\r\n")>=2 && gps_data.Data.count("$GNRMC")>=1 && gps_data.Data.count("$GNVTG")>=1)
    {
        //保存原始数据
        gps_data.set_src_data(gps_data.Data);
        char *gps_p;
        char gps_tmp_buff[50];
        gps_p=gps_data.Data.toLocal8Bit().data();
        //计算$GNRMC校验和
        if(GPS_GetCheckSum(gps_p,"$GNRMC")==0)
        {
            //qDebug()<<"$GNRMC 校验和计算成功.\n";
           // LogSend("$GNRMC 校验和计算成功\n");
            //提取定位状态数据
            GPS_GetData(gps_p,"$GNRMC",gps_tmp_buff,2);
            //判断定位数据是否有效
            if(gps_tmp_buff[0]=='A')
            {
                SendUartState(3); //发送信号 通知状态改变  定位成功
                //qDebug()<<"定位成功.";
                //提取纬度
                GPS_GetData(gps_p,"$GNRMC",gps_tmp_buff,3);
                QString lat=gps_tmp_buff;  //得到纬度
                //提取经度
                GPS_GetData(gps_p,"$GNRMC",gps_tmp_buff,5);
                QString lng=gps_tmp_buff;  //得到经度

               // qDebug()<<"当前纬度:"<<lat;
               //qDebug()<<"当前经度:"<<lng;

                unsigned int int_data;
                double s_Longitude,s_latitude;

                //转换纬度
                s_latitude=lat.toDouble();
                s_latitude=s_latitude/100;
                int_data=s_latitude;//得到纬度整数部分
                s_latitude=s_latitude-int_data;//得到纬度小数部分
                s_latitude=(s_latitude)*100;
                gps_data.set_lat(int_data+(s_latitude/60.0)); //得到转换后的值

                //转换经度
                s_Longitude=lng.toDouble();
                s_Longitude=s_Longitude/100;
                int_data=s_Longitude;//得到经度整数部分
                s_Longitude=s_Longitude-int_data; //得到经度小数部分
                s_Longitude=s_Longitude*100;

                //gai guo le
                gps_data.set_lng(int_data+(s_Longitude/60.0));
            }
            else
            {
                SendUartState(4); //发送信号 通知状态改变  定位失败
                //GPS经纬度置0
                gps_data.set_lat(0.0);
                gps_data.set_lng(0.0);
            }
        }
        else
        {
            //GPS经纬度置0
            gps_data.set_lat(0.0);
            gps_data.set_lng(0.0);
        }

        //计算GNVTG校验和
        if(GPS_GetCheckSum(gps_p,"$GNVTG")==0)
        {
            //提取速度
            GPS_GetData(gps_p,"$GNVTG",gps_tmp_buff,7);
            gps_data.set_speed(gps_tmp_buff);
        }
        else
        {
            gps_data.set_speed("0");//当前速度置0
        }
        //清空数据
        gps_data.Data.clear();
    }
}


//关闭串口
//1 数据传输串口连接 2 数据传输串口断开
void UART_ReadWriteThread::Close_UART(void)
{
    SendUartState(2); //发送信号 通知状态改变
    if(UART_Config->isOpen())
    {
       UART_Config->close();
    }
}

//发送数据
void UART_ReadWriteThread::UART_Send(QString text)
{
   //qDebug()<<text;
    //如果串口处于打开状态就发送数据
    if(UART_Config->isOpen())
    {
        QByteArray byte=text.toLocal8Bit();
        UART_Config->write(byte);
    }
}

/*
函数功能: 根据逗号位置提取数据
函数参数:
char *gps_src GPS源字符串地址
char *buff 存放提取的数据
char *find_head 查找的GPS数据头  例如:$GNVTG
int cnt  逗号的偏移量
返回值:提取的字节数。
*/
int UART_ReadWriteThread::GPS_GetData(const char *gps_src,const char *find_head,char *buff,int cnt)
{
    const char *p;
    int number=0; //提取的数量
    int a=0;
    p=strstr(gps_src,find_head);
    if(p!=nullptr)
    {
        //查找逗号的位置
        while(*p!='\0')
        {
            if(*p==',')a++; //记录逗号的数量
            if(a==cnt)break; //逗号找成功了
            p++;
        }
        p++; //跨过当前逗号
        //提取数据
        while(*p!='\0')
        {
            if(*p==',')break; //遇到逗号停止提取数据
            buff[number]=*p;
            p++;
            number++;
        }
        buff[number]='\0';
    }
    return number;
}

/*
 * 计算GPS数据指定数据的校验和
 * char *src_str GPS源字符串
 * char *find_str GPS数据头  例如:$GNVTG
 * 返回值: -1表示失败  0表示成功
*/
int UART_ReadWriteThread::GPS_GetCheckSum(const char *src_str,const char *find_str)
{
    const char *tmp_p;
    /*1. 开始解析数据*/
    tmp_p=strstr(src_str,find_str);
    if(tmp_p!=nullptr)
    {
        unsigned char data=0;
        tmp_p++;
        //计算校验和
        while(*tmp_p!='*' && *tmp_p!='\0')
        {
            data^=*tmp_p;
            tmp_p++;
        }
        QString sum_str=QString::number(data,16);
        QString sum_val;
        //提取校验和
        sum_val[0]=*(tmp_p+1);
        sum_val[1]=*(tmp_p+2);

        //qDebug()<<"sum_val:"<<sum_val;
        //qDebug()<<"sum_str:"<<sum_str;

        //判断校验和,不区分大小写
        if(sum_str.compare(sum_val,Qt::CaseInsensitive)==0)
        {
            return 0; //校验正确
        }
    }
    return -1;
}

3.3 widget.h代码

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QSerialPortInfo>
#include <QSerialPort>
#include <QPlainTextEdit>
#include <QScrollBar>
#include <QTimer>
#include <QThread>
#include <QDebug>
#include "uart_code.h"
#include "config.h"

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = NULL);
    ~Widget();
    void GetDeviceList();
    /*构造一个列表*/
    QList<QSerialPortInfo> usart_device_list;
    QSerialPort *UART_Config; //串口

    QTimer timer;
    qint8 connect_state=0; //串口连接状态
    //串口线程
    UART_ReadWriteThread uart_WorkClass; //串口读写-工作函数
    QThread uart_Workthread; //串口读写--线程
private slots:
    void Log_Text_Display(QString text);
    void Log_Text_Display2(QString text);
    void UART_State_Display(qint8 state);  //串口状态显示
    void update();

    void on_pushButton_2_clicked();

    void on_pushButton_clicked();

    void on_pushButton_send_data_clicked();

    void on_pushButton_mode_set_clicked();

    void on_pushButton_open_all_clicked();

    void on_pushButton_close_all_clicked();

    void on_pushButton_open_rmc_clicked();

    void on_pushButton_open_vtg_clicked();

signals:
    void Start_Uart_Read();
    void Uart_SendData(QString text);
private:
    Ui::Widget *ui;
};

#endif // WIDGET_H

3.4 widget.cpp代码

#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    this->setWindowTitle("GPS数据解析工具");
    GetDeviceList();

    connect(&timer, SIGNAL(timeout()), this, SLOT(update()));
    timer.start(2000);

    //串口初始化
    //连接串口的日志信息
    connect(&uart_WorkClass,SIGNAL(LogSend(QString)),this,SLOT(Log_Text_Display(QString)));
    connect(&uart_WorkClass,SIGNAL(SendUartState(qint8)),this,SLOT(UART_State_Display(qint8)));

    //连接串口初始化函数
    connect(this,SIGNAL(Start_Uart_Read()),&uart_WorkClass,SLOT(run()));
    //连接串口发送函数
    connect(this,SIGNAL(Uart_SendData(QString)),&uart_WorkClass,SLOT(UART_Send(QString)));
    //将串口工作对象移动到子线程里工作
    uart_WorkClass.moveToThread(&uart_Workthread);
}

Widget::~Widget()
{
    delete ui;
}


void Widget::GetDeviceList()
{
    ui->comboBox->clear();
    //获取系统串口设备
    usart_device_list=QSerialPortInfo::availablePorts();
    /*打印出支持的串口名称*/
    for(int i=0;i<usart_device_list.count();i++)
    {
        ui->comboBox->addItem(usart_device_list.at(i).portName());
    }
}

void Widget::on_pushButton_2_clicked()
{
    GetDeviceList();
}

void Widget::on_pushButton_clicked()
{
    current_SerialPort=ui->comboBox->currentText();  //当前串口端口号
    current_SerialBaudRate=ui->comboBox_baud->currentText().toInt(); //当前波特率

    if(ui->pushButton->text()=="打开串口")
    {
        if(current_SerialPort.isEmpty()||current_SerialBaudRate<=0)
        {
            QMessageBox::information(this,"提示","请先选择串口COM和波特率\n在菜单栏的设置页面找到串口设置按钮.",
            QMessageBox::Ok,QMessageBox::Ok);
        }
        else
        {
            ui->pushButton->setText("关闭串口");
            uart_Workthread.start(); //启动串口线程
            Start_Uart_Read(); //发送信号启动串口工作
        }
    }
    else
    {
        uart_WorkClass.Close_UART(); //关闭串口
        uart_Workthread.quit(); //退出线程
        uart_Workthread.wait(); //等待线程退出
        ui->pushButton->setText("打开串口");
    }
}

/*日志显示*/
void Widget::Log_Text_Display(QString text)
{
    QPlainTextEdit *plainTextEdit_log=ui->plainTextEdit;
    //设置光标到文本末尾
    plainTextEdit_log->moveCursor(QTextCursor::End, QTextCursor::MoveAnchor);
    //当文本数量超出一定范围就清除
    if(plainTextEdit_log->toPlainText().size()>4096)
    {
        plainTextEdit_log->clear();
    }
    plainTextEdit_log->insertPlainText(text);
    //移动滚动条到底部
    QScrollBar *scrollbar = plainTextEdit_log->verticalScrollBar();
    if(scrollbar)
    {
        scrollbar->setSliderPosition(scrollbar->maximum());
    }
}

/*日志显示*/
void Widget::Log_Text_Display2(QString text)
{
    QPlainTextEdit *plainTextEdit_log=ui->plainTextEdit2;
    //设置光标到文本末尾
    plainTextEdit_log->moveCursor(QTextCursor::End, QTextCursor::MoveAnchor);
    //当文本数量超出一定范围就清除
    if(plainTextEdit_log->toPlainText().size()>4096)
    {
        plainTextEdit_log->clear();
    }
    plainTextEdit_log->insertPlainText(text);
    //移动滚动条到底部
    QScrollBar *scrollbar = plainTextEdit_log->verticalScrollBar();
    if(scrollbar)
    {
        scrollbar->setSliderPosition(scrollbar->maximum());
    }
}

void Widget::update()
{
    QString text="";
    QString gps_text=gps_data.get_src_data();
    gps_data.set_src_clear(); //清除保存的原始数据

    gps_text=gps_text.remove("\r");
    gps_text=gps_text.remove("\n");
    //串口连接成功的情况下才需要打印
    if(connect_state==1)
    {
        text="GPS:";
        text+=tr("纬度=%1,").arg(gps_data.get_lat(),0,'g',13); //纬度
        text+=tr("经度=%1,").arg(gps_data.get_lng(),0,'g',13); //经度
        text+=tr("速度=%1\n").arg(gps_data.get_speed());//速度
        Log_Text_Display2(text);
        text+=tr("原始数据:%1\n").arg(gps_text);//原始数据

        Log_Text_Display2(text);
    }
}

//串口状态显示
//1 数据传输串口连接 2 数据传输串口断开
void Widget::UART_State_Display(qint8 state)
{
   if(state==1)
   {
       connect_state=state;
       Log_Text_Display("串口连接\n");
   }
   if(state==2)
   {
       connect_state=state;
       Log_Text_Display("串口断开\n");
   }
   if(state==3)  //GPS定位成功
   {
       ui->label_gps_stat->setText("GPS定位状态: 定位成功");
   }
   if(state==4)  //GPS定位失败
   {
       ui->label_gps_stat->setText("GPS定位状态: 正在定位中..请稍等");
   }
}

//发送数据
void Widget::on_pushButton_send_data_clicked()
{
    QString text=ui->lineEdit_send_data->text();
    if(ui->checkBox->isChecked())
    {
        text+="\r\n";
        Uart_SendData(text);
    }
    else
    {
        Uart_SendData(text);
    }
}

void Widget::on_pushButton_mode_set_clicked()
{
     Uart_SendData("$PCAS11,3*1E\r\n");
}

void Widget::on_pushButton_open_all_clicked()
{
    Uart_SendData("$CCRMO,GGA,4,1*38\r\n");
}

void Widget::on_pushButton_close_all_clicked()
{

    Uart_SendData("$CCRMO,GGA,3,1*3F\r\n");
}

void Widget::on_pushButton_open_rmc_clicked()
{

    Uart_SendData("$CCRMO,RMC,2,1*23\r\n");
}

void Widget::on_pushButton_open_vtg_clicked()
{
     Uart_SendData("$CCRMO,VTG,2,1*3A\r\n");
}

下面公众号有全套的单片机、QT、C++、C语言、物联网相关的教程,欢迎关注:

猜你喜欢

转载自blog.csdn.net/xiaolong1126626497/article/details/108080067