GPS通讯

来源:互联网 发布:mysql分区表 编辑:程序博客网 时间:2024/05/01 02:09

一:NMEA 协议

是为了在不同的GPS(全球定位系统)导航设备中建立统一的BTCM(海事无线电技术委员会)标准,由美国国家海洋电子协会(NMEA-The National Marine Electronics Associa-tion)制定的一套通讯协议。GPS接收机根据NMEA-0183协议的标准规范,将位置、速度等信息通过串口传送到PC机、PDA等 设备。

$GPGGA
例:$GPGGA,092204.999,4250.5589,S,14718.5084,E,1,04,24.4,19.7,M,,,,0000*1F
字段:$GPGGA,语句ID,表明该语句为Global Positioning System Fix Data(GGA)GPS定位信息
字段:UTC 时间,hhmmss.sss,时分秒格式
字段:纬度ddmm.mmmm,度分格式(前导位数不足则补)
字段:纬度N(北纬)或S(南纬)
字段:经度dddmm.mmmm,度分格式(前导位数不足则补)
字段:经度E(东经)或W(西经)
字段:GPS状态,=未定位,=非差分定位,=差分定位,=无效PPS,=正在估算
字段:正在使用的卫星数量( - 12)(前导位数不足则补)
字段:HDOP水平精度因子(.5 - 99.9)
字段:海拔高度(-9999.9 - 99999.9)
字段:地球椭球面相对大地水准面的高度
字段:差分时间(从最近一次接收到差分信号开始的秒数,如果不是差分定位将为空)
字段:差分站ID号 - 1023(前导位数不足则补,如果不是差分定位将为空)
字段:校验值

$GPGSA
例:$GPGSA,A,3,01,20,19,13,,,,,,,,,40.4,24.4,32.2*0A
字段:$GPGSA,语句ID,表明该语句为GPS DOP and Active Satellites(GSA)当前卫星信息
字段:定位模式,A=自动手动D/3D,M=手动D/3D
字段:定位类型,=未定位,=2D定位,=3D定位
字段:PRN码(伪随机噪声码),第信道正在使用的卫星PRN码编号()(前导位数不足则补)
字段:PRN码(伪随机噪声码),第信道正在使用的卫星PRN码编号()(前导位数不足则补)
字段:PRN码(伪随机噪声码),第信道正在使用的卫星PRN码编号()(前导位数不足则补)
字段:PRN码(伪随机噪声码),第信道正在使用的卫星PRN码编号()(前导位数不足则补)
字段:PRN码(伪随机噪声码),第信道正在使用的卫星PRN码编号()(前导位数不足则补)
字段:PRN码(伪随机噪声码),第信道正在使用的卫星PRN码编号()(前导位数不足则补)
字段:PRN码(伪随机噪声码),第信道正在使用的卫星PRN码编号()(前导位数不足则补)
字段:PRN码(伪随机噪声码),第信道正在使用的卫星PRN码编号()(前导位数不足则补)
字段:PRN码(伪随机噪声码),第信道正在使用的卫星PRN码编号()(前导位数不足则补)
字段:PRN码(伪随机噪声码),第信道正在使用的卫星PRN码编号()(前导位数不足则补)
字段:PRN码(伪随机噪声码),第信道正在使用的卫星PRN码编号()(前导位数不足则补)
字段:PRN码(伪随机噪声码),第信道正在使用的卫星PRN码编号()(前导位数不足则补)
字段:PDOP综合位置精度因子(.5 - 99.9)
字段:HDOP水平精度因子(.5 - 99.9)
字段:VDOP垂直精度因子(.5 - 99.9)
字段:校验值

$GPGSV
例:$GPGSV,3,1,10,20,78,331,45,01,59,235,47,22,41,069,,13,32,252,45*70
字段:$GPGSV,语句ID,表明该语句为GPS Satellites in View(GSV)可见卫星信息
字段:本次GSV语句的总数目( - 3)
字段:本条GSV语句是本次GSV语句的第几条( - 3)
字段:当前可见卫星总数( - 12)(前导位数不足则补)
字段:PRN 码(伪随机噪声码)( - 32)(前导位数不足则补)
字段:卫星仰角( - 90)度(前导位数不足则补)
字段:卫星方位角( - 359)度(前导位数不足则补)
字段:信噪比(-)dbHz
字段:PRN 码(伪随机噪声码)( - 32)(前导位数不足则补)
字段:卫星仰角( - 90)度(前导位数不足则补)
字段:卫星方位角( - 359)度(前导位数不足则补)
字段:信噪比(-)dbHz
字段:PRN 码(伪随机噪声码)( - 32)(前导位数不足则补)
字段:卫星仰角( - 90)度(前导位数不足则补)
字段:卫星方位角( - 359)度(前导位数不足则补)
字段:信噪比(-)dbHz
字段:校验值

$GPRMC
例:$GPRMC,024813.640,A,3158.4608,N,11848.3737,E,10.05,324.27,150706,,,A*50
字段:$GPRMC,语句ID,表明该语句为Recommended Minimum Specific GPS/TRANSIT Data(RMC)推荐最小定位信息
字段:UTC时间,hhmmss.sss格式
字段:状态,A=定位,V=未定位
字段:纬度ddmm.mmmm,度分格式(前导位数不足则补)
字段:纬度N(北纬)或S(南纬)
字段:经度dddmm.mmmm,度分格式(前导位数不足则补)
字段:经度E(东经)或W(西经)
字段:速度,节,Knots
字段:方位角,度
字段:UTC日期,DDMMYY格式
字段:磁偏角,( - 180)度(前导位数不足则补)
字段:磁偏角方向,E=东W=西
字段:校验值

$GPVTG
例:$GPVTG,89.68,T,,M,0.00,N,0.0,K*5F
字段:$GPVTG,语句ID,表明该语句为Track Made Good and Ground Speed(VTG)地面速度信息
字段:运动角度, - 359,(前导位数不足则补)
字段:T=真北参照系
字段:运动角度, - 359,(前导位数不足则补)
字段:M=磁北参照系
字段:水平运动速度(.00)(前导位数不足则补)
字段:N=节,Knots
字段:水平运动速度(.00)(前导位数不足则补)
字段:K=公里/时,km/h
字段:校验值

$GPGLL
例:$GPGLL,4250.5589,S,14718.5084,E,092204.999,A*2D
字段:$GPGLL,语句ID,表明该语句为Geographic Position(GLL)地理定位信息
字段:纬度ddmm.mmmm,度分格式(前导位数不足则补)
字段:纬度N(北纬)或S(南纬)
字段:经度dddmm.mmmm,度分格式(前导位数不足则补)
字段:经度E(东经)或W(西经)
字段:UTC时间,hhmmss.sss格式
字段:状态,A=定位,V=未定位
字段:校验值

二:GPS指定虚拟的串口号,就可以通过串口编程进行通讯了。主要过程:从串口读数据,找出完整的协议数据,然后将数据中的字段含义解析出来即可。

    if (pGps->HaveValidGPSData(pArray,strGps))
    {
        PGPSData pGpsData = NULL;
        pGpsData = pGps->AnalyseGpsData(strGps);
        if (pGpsData != NULL)
        {
            //将接收到的GPS数据填充到最新当前数据
            pGps->m_gpsCurData = (*pGpsData);
            //发送接收有效GPS位置信息WINDOWS消息通知
            //由消息处理函数释放内存
            ::PostMessage(pGps->m_pWnd->m_hWnd,WM_GPS_RECV_VALID_LONGLAT,WPARAM(pGpsData),0);
        }
    }

1:串口通讯类

    //定义串口接收数据函数类型
    typedef void (CALLBACK* ONSERIESREAD)(void * pOwner /*父对象指针*/
                                          ,BYTE* buf  /*接收到的缓冲区*/
                                          ,DWORD dwBufLen /*接收到的缓冲区长度*/);


    class CCESeries
    {
    public:
        CCESeries(void);
        ~CCESeries(void);
    public:
        //打开串口
        BOOL OpenPort(void* pOwner,/*指向父指针*/
            UINT portNo    = 1,        /*串口号*/
            UINT baud        = 9600,    /*波特率*/
            UINT parity    = NOPARITY, /*奇偶校验*/
            UINT databits    = 8,        /*数据位*/
            UINT stopbits    = 0        /*停止位*/
            );
        //关闭串口
        void ClosePort();
        //同步写入数据
        BOOL WriteSyncPort(const BYTE*buf , DWORD bufLen);
        //设置串口读取、写入超时
        BOOL SetSeriesTimeouts(COMMTIMEOUTS CommTimeOuts);
        //得到串口是否打开
        BOOL GetComOpened();
    private:
        //串口读线程函数
        static  DWORD WINAPI ReadThreadFunc(LPVOID lparam);
    private:
        //关闭读线程
        void CloseReadThread();
    private:
        //已打开的串口句柄
        HANDLE    m_hComm;
        //读线程句柄
        HANDLE m_hReadThread;
        //读线程ID标识
        DWORD m_dwReadThreadID;
        //读线程退出事件
        HANDLE m_hReadCloseEvent;
        BOOL m_bOpened; //串口是否打开
        void * m_pOwner; //指定父对象指针
    public:
        ONSERIESREAD m_OnSeriesRead; //串口读取回调函数
    };
    //构造函数
    CCESeries::CCESeries()
    {
        //初始化内部变量
        m_hComm = INVALID_HANDLE_VALUE;
        m_OnSeriesRead = NULL;
        m_bOpened = 0;
    }

    //析构函数
    CCESeries::~CCESeries()
    {
        if (m_bOpened)
        {
            //关闭串口
            ClosePort();
        }
    }

    //串口读线程函数
    DWORD CCESeries::ReadThreadFunc(LPVOID lparam)
    {
        CCESeries *ceSeries = (CCESeries*)lparam;

        DWORD    evtMask;
        BYTE * readBuf = NULL;//读取的字节
        DWORD actualReadLen=0;//实际读取的字节数
        DWORD willReadLen;

        DWORD dwReadErrors;
        COMSTAT    cmState;

        // 清空缓冲,并检查串口是否打开。
        ASSERT(ceSeries->m_hComm !=INVALID_HANDLE_VALUE);


        //清空串口
        PurgeComm(ceSeries->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR );

        SetCommMask (ceSeries->m_hComm, EV_RXCHAR | EV_CTS | EV_DSR );
        while (TRUE)
        {      
            if (WaitCommEvent(ceSeries->m_hComm,&evtMask,0))
            {           
                SetCommMask (ceSeries->m_hComm, EV_RXCHAR | EV_CTS | EV_DSR );
                //表示串口收到字符       
                if (evtMask & EV_RXCHAR)
                {
                    ClearCommError(ceSeries->m_hComm,&dwReadErrors,&cmState);
                    willReadLen = cmState.cbInQue ;
                    if (willReadLen <= 0)
                    {
                        continue;
                    }

                    //分配内存
                    readBuf = new BYTE[willReadLen];
                    ZeroMemory(readBuf,willReadLen);
                    //读取串口数据
                    ReadFile(ceSeries->m_hComm, readBuf, willReadLen, &actualReadLen,0);

                    //如果读取的数据大于,
                    if (actualReadLen>0)
                    {
                        //触发读取回调函数
                        if (ceSeries->m_OnSeriesRead)
                        {
                            ceSeries->m_OnSeriesRead(ceSeries->m_pOwner,readBuf,actualReadLen);
                        }
                    }

                    //释放内存
                    delete[] readBuf;
                    readBuf = NULL;
                }
            }
            //如果收到读线程退出信号,则退出线程
            if (WaitForSingleObject(ceSeries->m_hReadCloseEvent,500) == WAIT_OBJECT_0)
            {
                break;
            }
        }
        return 0;
    }

    //关闭读线程
    void CCESeries::CloseReadThread()
    {
        SetEvent(m_hReadCloseEvent);
        //设置所有事件无效无效
        SetCommMask(m_hComm, 0);
        //清空所有将要读的数据
        PurgeComm( m_hComm,  PURGE_RXCLEAR );
        //等待秒,如果读线程没有退出,则强制退出
        if (WaitForSingleObject(m_hReadThread,4000) == WAIT_TIMEOUT)
        {
            TerminateThread(m_hReadThread,0);
        }
        m_hReadThread = NULL;
    }

    /*
    *函数介绍:打开串口
    *入口参数:pPortOwner    :使用此串口类的窗体句柄
    portNo        :串口号
    baud            :波特率
    parity        :奇偶校验
    databits        :数据位
    stopbits        :停止位
    *出口参数:(无)
    *返回值:TRUE:成功打开串口;FALSE:打开串口失败
    */
    BOOL CCESeries::OpenPort(void * pOwner,
                             UINT portNo    ,            /*串口号*/
                             UINT baud        ,            /*波特率*/
                             UINT parity    ,            /*奇偶校验*/
                             UINT databits    ,            /*数据位*/
                             UINT stopbits               /*停止位*/
                             )
    {
        DCB commParam;
        TCHAR szPort[15];   

        ASSERT(pOwner!=NULL);
        m_pOwner = pOwner;

        // 已经打开的话,直接返回
        if (m_hComm != INVALID_HANDLE_VALUE)
        {
            return TRUE;
        }

        //设置串口名
        wsprintf(szPort, L"COM%d:", portNo);
        //打开串口
        m_hComm = CreateFile(
            szPort,
            GENERIC_READ | GENERIC_WRITE,    //允许读和写
            0,                                //独占方式(共享模式)
            NULL,
            OPEN_EXISTING,                    //打开而不是创建(创建方式)
            0,
            NULL
            );

        if (m_hComm == INVALID_HANDLE_VALUE)
        {
            // 无效句柄,返回。       
            TRACE(_T("CreateFile 返回无效句柄/n"));
            return FALSE;

        }

        // 得到打开串口的当前属性参数,修改后再重新设置串口。
        if (!GetCommState(m_hComm,&commParam))
        {       
            //关闭串口
            CloseHandle (m_hComm);
            m_hComm = INVALID_HANDLE_VALUE;
            return FALSE;
        }

        //设置串口参数
        commParam.BaudRate = baud;                    // 设置波特率
        commParam.fBinary = TRUE;                    // 设置二进制模式,此处必须设置TRUE
        commParam.fParity = TRUE;                    // 支持奇偶校验
        commParam.ByteSize = databits;                // 数据位,范围:4-8
        commParam.Parity = parity;                // 校验模式
        commParam.StopBits = stopbits;                // 停止位

        commParam.fOutxCtsFlow = FALSE;                // No CTS output flow control
        commParam.fOutxDsrFlow = FALSE;                // No DSR output flow control
        commParam.fDtrControl = DTR_CONTROL_ENABLE;
        // DTR flow control type
        commParam.fDsrSensitivity = FALSE;            // DSR sensitivity
        commParam.fTXContinueOnXoff = TRUE;            // XOFF continues Tx
        commParam.fOutX = FALSE;                    // No XON/XOFF out flow control
        commParam.fInX = FALSE;                        // No XON/XOFF in flow control
        commParam.fErrorChar = FALSE;                // Disable error replacement
        commParam.fNull = FALSE;                    // Disable null stripping
        commParam.fRtsControl = RTS_CONTROL_ENABLE;
        // RTS flow control
        commParam.fAbortOnError = FALSE;            // 当串口发生错误,并不终止串口读写

        //设置串口参数
        if (!SetCommState(m_hComm, &commParam))
        {
            TRACE(_T("SetCommState error"));   
            //关闭串口
            CloseHandle (m_hComm);
            m_hComm = INVALID_HANDLE_VALUE;       
            return FALSE;
        }

        //设置串口读写时间
        COMMTIMEOUTS CommTimeOuts;
        GetCommTimeouts (m_hComm, &CommTimeOuts);
        CommTimeOuts.ReadIntervalTimeout = MAXDWORD; 
        CommTimeOuts.ReadTotalTimeoutMultiplier = 0; 
        CommTimeOuts.ReadTotalTimeoutConstant = 0;   
        CommTimeOuts.WriteTotalTimeoutMultiplier = 10; 
        CommTimeOuts.WriteTotalTimeoutConstant = 1000; 
        if(!SetCommTimeouts( m_hComm, &CommTimeOuts ))
        {
            TRACE( _T("SetCommTimeouts 返回错误") );
            //关闭串口
            CloseHandle (m_hComm);

            m_hComm = INVALID_HANDLE_VALUE;
            return FALSE;
        }

        //指定端口监测的事件集
        SetCommMask (m_hComm, EV_RXCHAR);
        //分配串口设备缓冲区
        SetupComm(m_hComm,512,512);
        //初始化缓冲区中的信息
        PurgeComm(m_hComm,PURGE_TXCLEAR|PURGE_RXCLEAR);

        CString strEvent;
        strEvent.Format(L"Com_ReadCloseEvent%d",portNo);
        m_hReadCloseEvent = CreateEvent(NULL,TRUE,FALSE,strEvent);

        //创建串口读数据监听线程
        m_hReadThread = CreateThread(NULL,0,ReadThreadFunc,this,0,&m_dwReadThreadID);

        TRACE(_T("串口打开成功"));
        m_bOpened = TRUE;
        return TRUE;
    }

    /*
    *函数介绍:关闭串口
    *入口参数:(无)
    *出口参数:(无)
    *返回值:(无)
    */
    void CCESeries::ClosePort()
    {   
        //表示串口还没有打开
        if (m_hComm == INVALID_HANDLE_VALUE)
        {
            return ;
        }

        //关闭读线程
        CloseReadThread();

        //关闭串口
        CloseHandle (m_hComm);
        //关闭事件
        CloseHandle(m_hReadCloseEvent);

        m_hComm = INVALID_HANDLE_VALUE;
        m_bOpened = FALSE;
    }

    /*
    *函数介绍:往串口写入数据
    *入口参数:buf :待写入数据缓冲区
    bufLen : 待写入缓冲区长度
    *出口参数:(无)
    *返回值:TRUE:设置成功;FALSE:设置失败
    */
    BOOL CCESeries::WriteSyncPort(const BYTE*buf , DWORD bufLen)
    {
        DWORD dwNumBytesWritten;
        DWORD dwHaveNumWritten =0 ; //已经写入多少

        int iInc = 0; //如果次写入不成功,返回FALSE
        ASSERT(m_hComm != INVALID_HANDLE_VALUE);
        do
        {
            if (WriteFile (m_hComm,                    //串口句柄
                buf+dwHaveNumWritten,                //被写数据缓冲区
                bufLen - dwHaveNumWritten,          //被写数据缓冲区大小
                &dwNumBytesWritten,                    //函数执行成功后,返回实际向串口写的个数   
                NULL))                                //此处必须设置NULL
            {
                dwHaveNumWritten = dwHaveNumWritten + dwNumBytesWritten;
                //写入完成
                if (dwHaveNumWritten == bufLen)
                {
                    break;
                }
                iInc++;
                if (iInc >= 3)
                {
                    return FALSE;
                }
                Sleep(10);
            }
            else
            {
                return FALSE;
            }
        }while (TRUE);

        return TRUE;       
    }

    /*
    *函数介绍:设置串口读取、写入超时
    *入口参数:CommTimeOuts : 指向COMMTIMEOUTS结构
    *出口参数:(无)
    *返回值:TRUE:设置成功;FALSE:设置失败
    */
    BOOL CCESeries::SetSeriesTimeouts(COMMTIMEOUTS CommTimeOuts)
    {
        ASSERT(m_hComm != INVALID_HANDLE_VALUE);
        return SetCommTimeouts(m_hComm,&CommTimeOuts);
    }


    //得到串口是否打开
    BOOL CCESeries::GetComOpened()
    {
        return m_bOpened;
    }

2:GPRS通讯类

    #include "CESeries.h"

    //定义GPS数据接收显示
    #define WM_GPS_RECV_BUF                WM_USER + 101
    //定义GPS状态改变消息
    #define WM_GPS_STATE_CHANGE_MESSAGE    WM_USER + 102
    //定义收到正确的GPS位置信息
    #define WM_GPS_RECV_VALID_LONGLAT    WM_USER + 103

    //定义GPS设备状态常量
    enum GPSDEV_STATE
    {
        GPS_VALID_DATA = 0,   //获取有效数据
        GPS_INVALID_DATA,//获取无效数据
        GPS_DEV_NOTOPENED,  //GPS串口未打开
        GPS_DEV_OPENED, //GPS串口已打开
        GPS_NODATA//GPS未收到数据
    };

    //GPS数据结构
    typedef struct _GPSData
    {
        char date[11] ; //Gps数据日期
        char time[9] ;  //Gps数据时间
        char latitude_type[2]; //纬度类型,北纬,南纬
        char latitude[10] ; //纬度值
        char longitude_type[2]; //经度类型,东经,西经
        char longitude[11] ;//经度值
        char speed[6];//速度
        char starNum; //卫星数目
    }GPSData,*PGPSData;

    class CGPS
    {
    public:
        CGPS(void);
        ~CGPS(void);
    public:
        //打开GPS设备
        BOOL Open(       CWnd *pWnd , /*拥有者窗口句柄*/
            UINT portNo    = 1,        /*串口号*/
            UINT baud        = 9600,    /*波特率*/
            UINT parity    = NOPARITY, /*奇偶校验*/
            UINT databits    = 8,        /*数据位*/
            UINT stopbits    = 0 );
        //关闭GPS设备
        void Close();
        //获取GPS设备状态
        GPSDEV_STATE GetGpsState();
        //得到当前GPS数据
        GPSData GetCurGpsData();

    private:
        //在缓冲区中查找子字符串
        int Pos(LPCSTR subString , CByteArray * pArray,int iPos);
        //判断是否存在有效GPS数据
        BOOL HaveValidGPSData(CByteArray * pArray,/*分析的缓冲区队列*/
            CString &outStr);
        //解析GPS数据
        PGPSData AnalyseGpsData(CString &aRecvStr);
    private:
        //串口接收数据回调函数
        static void CALLBACK GpsOnSeriesRead(void* pOwner,BYTE* buf,DWORD dwBufLen);
    private:
        //GPS数据检测线程函数
        static DWORD WINAPI GpsCheckThreadFunc(LPVOID lparam);
    private:
        GPSDEV_STATE m_gpsDev_State; //GPS当前设备状态
        GPSData  m_gpsCurData;       // GPS当前数据
        GPSData m_gpsLastData;        //GPS上一次数据
        CCESeries m_ceSeries;        //GPS串口通讯类
        CByteArray  m_aRecvBuf  ;   //接收缓冲区
        CWnd *m_pWnd; //存储主窗体句柄
        HANDLE m_hThreadQuitEvent;    //线程退出事件
        HANDLE m_hGpsThread;        //GPS检测线程句柄
        DWORD m_dwGpsThreadID;        //GPS检测线程标识
    };

    //构造函数
    CGPS::CGPS()
    {
        m_gpsDev_State = GPS_DEV_NOTOPENED; //GPS状态
        m_hGpsThread = NULL;                //GPS检测线程句柄
        ZeroMemory(&m_gpsCurData,sizeof(m_gpsCurData));  //GPS当前数据
        ZeroMemory(&m_gpsLastData,sizeof(m_gpsLastData)); //GPS上一次数据
    }

    //析构函数
    CGPS::~CGPS(void)
    {
    }

    /*
    *函数介绍:打开GPS设备
    *入口参数:pWnd    :使用此GPS类的窗体句柄
    portNo        :串口号
    baud            :波特率
    parity        :奇偶校验
    databits        :数据位
    stopbits        :停止位
    *出口参数:(无)
    *返回值:TRUE:成功打开GPS设备;FALSE:打开GPS设备失败
    */
    BOOL CGPS::Open(CWnd *pWnd , /*拥有者窗口句柄*/
                    UINT portNo,        /*串口号*/
                    UINT baud,    /*波特率*/
                    UINT parity, /*奇偶校验*/
                    UINT databits,        /*数据位*/
                    UINT stopbits    /*停止位*/
                    )
    {
        m_pWnd = pWnd;  //储存窗口句柄
        //创建GPS检测线程退出事件
        m_hThreadQuitEvent = CreateEvent(NULL,false,false,L"EVENT_GPS_THREAD");
        //指定串口读回调函数
        m_ceSeries.m_OnSeriesRead = GpsOnSeriesRead;
        //打开GPS设备串口
        BOOL bResult = m_ceSeries.OpenPort(this,portNo,baud,parity,databits,stopbits);

        if (bResult)
        {
            //设置当前GPS状态
            m_gpsDev_State = GPS_DEV_OPENED;
            //发送GPS状态变化消息
            ::PostMessage(m_pWnd->m_hWnd,WM_GPS_STATE_CHANGE_MESSAGE,WPARAM(GPS_DEV_OPENED),1);

            //创建GPS状态检测线程
            m_hGpsThread = CreateThread(NULL,0,GpsCheckThreadFunc,this,0,&m_dwGpsThreadID);
        }
        else
        {
            //设置当前GPS状态
            m_gpsDev_State = GPS_DEV_NOTOPENED;
            //发送GPS状态变化消息
            ::PostMessage(m_pWnd->m_hWnd,WM_GPS_STATE_CHANGE_MESSAGE,WPARAM(GPS_DEV_NOTOPENED),1);
        }
        return bResult;
    }

    /*
    *函数介绍:关闭GPS设备
    *入口参数:(无)
    *出口参数:(无)
    *返回值:TRUE:成功关闭GPS设备;FALSE:关闭GPS设备失败
    */
    void CGPS::Close()
    {
        //先退出GPS检测线程
        if (m_hGpsThread != NULL)
        {
            //发送线程退出信号
            SetEvent(m_hThreadQuitEvent);
            //等待线程退出
            if (WaitForSingleObject(m_hGpsThread,1000) == WAIT_TIMEOUT)
            {
                TerminateThread(m_hGpsThread,0);
            }
        }

        m_hGpsThread = NULL;
        CloseHandle(m_hThreadQuitEvent);
        //将接收数据回掉函数置空
        m_ceSeries.m_OnSeriesRead = NULL;
        //关闭GPS串口
        m_ceSeries.ClosePort();
        //设置GPS状态
        m_gpsDev_State = GPS_DEV_NOTOPENED;
        //发送GPS状态变化消息
        ::PostMessage(m_pWnd->m_hWnd,WM_GPS_STATE_CHANGE_MESSAGE,WPARAM(GPS_DEV_NOTOPENED),1);
    }

    /*
    *函数介绍:获取GPS设备状态
    *入口参数:(无)
    *出口参数:(无)
    *返回值:返回GPS设备状态
    */
    GPSDEV_STATE CGPS::GetGpsState()
    {
        return m_gpsDev_State;
    }


    /*
    *函数介绍:得到当前GPS数据
    *入口参数:(无)
    *出口参数:(无)
    *返回值:返回GPS设备当前GPS数据
    */
    GPSData CGPS::GetCurGpsData()
    {
        return m_gpsCurData;
    }

    /*--------------------------------------------------------------------
    【函数介绍】: 在pArray缓冲区,查找subString字符串,如存在,返回当前位置,否则返回-1
    【入口参数】: pArray:指定接收到的缓冲区队列
    【出口参数】: pArray:指定接收到的缓冲区队列,解析后需要进行适当修改
    【返回值】: -1表示没有找到指定的子串,>=0表示发现第个子串的位置
    ---------------------------------------------------------------------*/
    int CGPS::Pos(LPCSTR subString , CByteArray * pArray,int iPos)
    {
        //得到子串长度
        int subLen = strlen(subString);
        //得到缓冲区的长度
        int bufLen = pArray->GetUpperBound()+1;

        bool aResult = TRUE;
        //
        for ( int i=iPos;i<bufLen-subLen+1;i++)
        {
            aResult = TRUE;
            for (int j=0;j<subLen;j++)
            {
                if (pArray->GetAt(i+j) != *(subString + j))
                {
                    aResult = FALSE;
                    break;
                }
                int k = 0;
            }
            if (aResult)
            {
                return i;
            }
        }
        return -1;
    }


    /*
    *函数介绍:判断是否存在有效GPS数据
    *入口参数:aRecvStr :缓冲数据
    *出口参数:aRecvStr : 缓冲数据,outStr:得到的一个完整的GPS数据
    *返回值:TRUE : 成功初始化, FALSE : 初始化失败
    */
    BOOL CGPS::HaveValidGPSData(CByteArray * pArray,CString &outStr)
    {
        int tmpPos1,tmpPos2;

        tmpPos1 = Pos("$GPRMC",pArray,0);

        tmpPos2 = Pos("$GPRMC",pArray,tmpPos1+6);

        if (tmpPos2 >= 0)  //代表已包含两个$GPRMC
        {  
            if (tmpPos1 >= 0 )
            {
                BYTE *pBuf = pArray->GetData();
                char *sBuf = new char[tmpPos2-tmpPos1+1];
                ZeroMemory(sBuf,tmpPos2-tmpPos1+1);
                CopyMemory(sBuf,pBuf+tmpPos1,tmpPos2-tmpPos1+1);
                outStr = CString(sBuf);

                //释放内存
                delete[] sBuf;
                sBuf = NULL;
                pArray->RemoveAt(0,tmpPos2);
                return TRUE;
            }
        }
        return FALSE;
    }

    /*
    *函数介绍:解析GPS数据
    *入口参数:aRecvStr :指待解析的GPS缓冲数据
    *出口参数:(无)
    *返回值:指CGPSData结构体的指针,如果无效即为:NULL;
    */
    PGPSData CGPS::AnalyseGpsData(CString &aRecvStr)
    {
        CString tmpTime;
        CString tmpState;
        CString tmpDate;
        CString tmpLONG;
        CString tmpLONGType;
        CString tmpLAT;
        CString tmpLATType;
        CString tmpSpeed;

        LPSTR pStrDate = NULL;
        LPSTR pStrTime = NULL;
        LPSTR pStrLong = NULL;
        LPSTR pStrLongType = NULL;
        LPSTR pStrLat = NULL;
        LPSTR pStrLatType = NULL;
        LPSTR pStrSpeed = NULL;

        PGPSData pGpsData = NULL;
        int tmpPos,tmpPos1;
        int len;

        tmpPos = aRecvStr.Find(',',0); //第个值
        tmpPos1 = aRecvStr.Find(',',tmpPos+1);

        //得到时间
        tmpTime = aRecvStr.Mid(tmpPos+1,tmpPos1-tmpPos-1);
        tmpTime = tmpTime.Mid(0,2)+L":"+tmpTime.Mid(2,2)+L":"+tmpTime.Mid(4,2);

        len = tmpTime.GetLength();
        pStrTime = LPSTR(LocalAlloc(LMEM_ZEROINIT,len));
        WideCharToMultiByte(CP_ACP,WC_COMPOSITECHECK,tmpTime.GetBuffer(len),len
            ,pStrTime,len ,NULL,NULL);

        //数据状态,是否有效
        tmpPos = aRecvStr.Find(',',tmpPos+1);  //第个值
        tmpPos1 = aRecvStr.Find(',',tmpPos+1);
        tmpState = aRecvStr.Mid(tmpPos+1,tmpPos1-tmpPos-1);

        if (tmpState != 'A')//代表数据无效,返回
        {
            if (m_gpsDev_State != GPS_INVALID_DATA)
            {
                //设置GPS状态
                m_gpsDev_State = GPS_INVALID_DATA;
                //发送GPS状态变化消息
                ::PostMessage(m_pWnd->m_hWnd,WM_GPS_STATE_CHANGE_MESSAGE,WPARAM(GPS_INVALID_DATA),1);
            }
            LocalFree(pStrTime);
            return NULL;
        }
        else  //代表数据有效
        {
            if (m_gpsDev_State != GPS_VALID_DATA)
            {
                //设置GPS状态
                m_gpsDev_State = GPS_VALID_DATA;
                //发送GPS状态变化消息
                ::PostMessage(m_pWnd->m_hWnd,WM_GPS_STATE_CHANGE_MESSAGE,WPARAM(GPS_VALID_DATA),1);
            }
        }

        //得到纬度值
        tmpPos = aRecvStr.Find(',',tmpPos+1);//第个值
        tmpPos1 = aRecvStr.Find(',',tmpPos+1);
        tmpLAT    = aRecvStr.Mid(tmpPos+1,tmpPos1-tmpPos-1);

        len = tmpLAT.GetLength();
        pStrLat = LPSTR(LocalAlloc(LMEM_ZEROINIT,len));
        WideCharToMultiByte(CP_ACP,WC_COMPOSITECHECK,tmpLAT.GetBuffer(len),len
            ,pStrLat,len ,NULL,NULL);

        tmpPos = aRecvStr.Find(',',tmpPos+1);//第个值
        tmpPos1 = aRecvStr.Find(',',tmpPos+1);
        tmpLATType = aRecvStr.Mid(tmpPos+1,tmpPos1-tmpPos-1);

        len = tmpLATType.GetLength();
        pStrLatType = LPSTR(LocalAlloc(LMEM_ZEROINIT,len));
        WideCharToMultiByte(CP_ACP,WC_COMPOSITECHECK,tmpLATType.GetBuffer(len),len
            ,pStrLatType,len ,NULL,NULL);

        //得到经度值
        tmpPos = aRecvStr.Find(',',tmpPos+1);//第个值
        tmpPos1 = aRecvStr.Find(',',tmpPos+1);
        tmpLONG = aRecvStr.Mid(tmpPos+1,tmpPos1-tmpPos-1);

        len = tmpLONG.GetLength();
        pStrLong = LPSTR(LocalAlloc(LMEM_ZEROINIT,len));
        WideCharToMultiByte(CP_ACP,WC_COMPOSITECHECK,tmpLONG.GetBuffer(len),len
            ,pStrLong,len ,NULL,NULL);

        tmpPos = aRecvStr.Find(',',tmpPos+1);//第个值
        tmpPos1 = aRecvStr.Find(',',tmpPos+1);
        tmpLONGType = aRecvStr.Mid(tmpPos+1,tmpPos1-tmpPos-1);

        len = tmpLONGType.GetLength();
        pStrLongType = LPSTR(LocalAlloc(LMEM_ZEROINIT,len));
        WideCharToMultiByte(CP_ACP,WC_COMPOSITECHECK,tmpLONGType.GetBuffer(len),len
            ,pStrLongType,len ,NULL,NULL);

        //得到车速
        tmpPos = aRecvStr.Find(',',tmpPos+1);////第个值
        tmpPos1 = aRecvStr.Find(',',tmpPos+1);
        tmpSpeed = aRecvStr.Mid(tmpPos+1,tmpPos1-tmpPos-1);

        len = tmpSpeed.GetLength();
        pStrSpeed = LPSTR(LocalAlloc(LMEM_ZEROINIT,len));
        WideCharToMultiByte(CP_ACP,WC_COMPOSITECHECK,tmpSpeed.GetBuffer(len),len
            ,pStrSpeed,len ,NULL,NULL);

        tmpPos = aRecvStr.Find(',',tmpPos+1);////第个值

        //得到日期
        tmpPos = aRecvStr.Find(',',tmpPos+1);////第个值
        tmpPos1 = aRecvStr.Find(',',tmpPos+1);
        //格式化一下
        tmpDate = aRecvStr.Mid(tmpPos+1,tmpPos1-tmpPos-1);
        tmpDate = L"20"+tmpDate.Mid(4,2)+L"-"+tmpDate.Mid(2,2)+L"-"+tmpDate.Mid(0,2);

        len = tmpDate.GetLength();
        pStrDate = LPSTR(LocalAlloc(LMEM_ZEROINIT,len));
        WideCharToMultiByte(CP_ACP,WC_COMPOSITECHECK,tmpDate.GetBuffer(len),len
            ,pStrDate,len ,NULL,NULL);

        pGpsData = new GPSData();
        ZeroMemory(pGpsData,sizeof(GPSData));
        //得到GPS数据指针
        CopyMemory(pGpsData->date,pStrDate,10);
        CopyMemory(pGpsData->time,pStrTime,8);
        CopyMemory(pGpsData->latitude_type,pStrLatType,1);
        CopyMemory(pGpsData->latitude,pStrLat,9);
        CopyMemory(pGpsData->longitude_type,pStrLongType,1);
        CopyMemory(pGpsData->longitude,pStrLong,10);
        //先置默认速度
        FillMemory(pGpsData->speed,5,'0');
        CopyMemory(pGpsData->speed,pStrSpeed,5);

        //释放内存
        LocalFree(pStrTime);
        LocalFree(pStrDate);
        LocalFree(pStrLatType);
        LocalFree(pStrLat);
        LocalFree(pStrLongType);
        LocalFree(pStrLong);
        LocalFree(pStrSpeed);

        return pGpsData;
    }

    //GPS接收数据事件
    void CALLBACK CGPS::GpsOnSeriesRead(void * powner,BYTE* buf,DWORD  dwBufLen)
    {
        CGPS * pGps = (CGPS*)powner;
        //得到本类指针
        CByteArray * pArray = &(pGps->m_aRecvBuf);

        //得到缓冲区大小
        int iMaxSize = pArray->GetSize();
        //得到缓冲区所使用的大小
        int iUpperBound = pArray->GetUpperBound();
        for (int i=0;i<dwBufLen;i++)
        {
            pArray->Add(*(buf+i));
        }

        //将收到的数据发给主程序显示出来
        char* pRecvBuf = new char[dwBufLen+1];
        ZeroMemory(pRecvBuf,dwBufLen+1);
        CopyMemory(pRecvBuf,buf,dwBufLen);

        //发送接收串口原始数据WINDOWS消息通知
        //消息处理完毕后,应释放内存
        ::PostMessage(pGps->m_pWnd->m_hWnd,WM_GPS_RECV_BUF,WPARAM(pRecvBuf),dwBufLen+1);

        CString strGps;
        //检查是否已经存在有效的GPS数据
        if (pGps->HaveValidGPSData(pArray,strGps))
        {
            PGPSData pGpsData = NULL;
            pGpsData = pGps->AnalyseGpsData(strGps);
            if (pGpsData != NULL)
            {
                //将接收到的GPS数据填充到最新当前数据
                pGps->m_gpsCurData = (*pGpsData);
                //发送接收有效GPS位置信息WINDOWS消息通知
                //由消息处理函数释放内存
                ::PostMessage(pGps->m_pWnd->m_hWnd,WM_GPS_RECV_VALID_LONGLAT,WPARAM(pGpsData),0);
            }
        }
    }

    //检测GPS当前数据
    DWORD WINAPI CGPS::GpsCheckThreadFunc(LPVOID lparam)
    {
        //得到当前GPS指针
        CGPS *pGps = (CGPS*)lparam;

        int iRecCount = 0;
        //然后开始做循环检测,间隔为秒
        while (TRUE)
        {
            //判断两次收到的时间是否相同
            if (strcmp(pGps->m_gpsCurData.time,pGps->m_gpsLastData.time) == 0)
            {
                //计数加
                iRecCount++;
            }
            else
            {
                //将当前的GPS数据赋给历史值
                pGps->m_gpsLastData = pGps->m_gpsCurData;
                iRecCount = 0 ;
            }

            //代表连续三次没有收到数据
            if (iRecCount == 3)
            {
                if (pGps->m_gpsDev_State != GPS_NODATA)
                {
                    //将GPS状态置为“无数据”
                    pGps->m_gpsDev_State = GPS_NODATA;
                    //发送GPS状态改变消息
                    ::PostMessage(pGps->m_pWnd->m_hWnd,WM_GPS_STATE_CHANGE_MESSAGE,WPARAM(GPS_NODATA),1);
                }
            }

            //延时秒
            for (int i =0; i<10;i++)
            {
                //线程退出
                if (WaitForSingleObject(pGps->m_hThreadQuitEvent,100) == WAIT_OBJECT_0)
                {
                    goto finish;
                }
            }
        }
    finish:
        TRACE(L"GPS 检测线程退出/n");
        return 0;
    }

3:操作类

    CGPS *m_pGps;  //GPS类对象
    // 显示GPS串口接收的数据
    afx_msg LONG OnRecvSerialData(WPARAM wParam,LPARAM lParam);
    // 显示GPS状态变化
    afx_msg LONG OnGpsStateChange(WPARAM wParam,LPARAM lParam);
    // 显示GPS有效数据
    afx_msg LONG OnRecvValidGps(WPARAM wParam,LPARAM lParam);

    ON_MESSAGE(WM_GPS_RECV_BUF,OnRecvSerialData)
    ON_MESSAGE(WM_GPS_STATE_CHANGE_MESSAGE,OnGpsStateChange)
    ON_MESSAGE(WM_GPS_RECV_VALID_LONGLAT,OnRecvValidGps)
    打开GPS
    //先检查是否释放
    if (m_pGps != NULL)
    {
        m_pGps->Close();
        delete m_pGps;
        m_pGps = NULL;
    }
    //创建GPS逻辑类
    m_pGps = new CGPS();
    //
    if (m_pGps->Open(this,GPS_COM_NO,115200))
    {
        AfxMessageBox(L"GPS设备打开成功");
    }
    else
    {
        AfxMessageBox(L"GPS 设备打开失败");
        return ;
    }
    关闭GPRS
    //释放GPS对象
    if (m_pGps != NULL)
    {
        m_pGps->Close();
        delete m_pGps;
        m_pGps = NULL;
    }

三:GPS解析
//获得GPS参数
//注意:从GPS接收到的字符串已经在m_strRecv中,由于是定时接收,所以在这个字符串的头和尾都可能存在
//     不完整的NMEA输出字符串,在处理时要特别注意
//返回:TRUE(格式正确);FALSE(格式错误)
BOOL CGPSDlg::GetGPSParam()
{
    int i,j;
    CString str,strNEMA;

    //先判断是否接收到数据
    if (m_strRecv.IsEmpty())
        return FALSE;

    //若字符串不是以'$'开头的,必须删掉这部分不完整的
    if (m_strRecv[0] != '$')
    {
        i = m_strRecv.Find('/n', 0);
        if (i == -1)
            return FALSE;                                       //尾部未接收完整,必须等接收完后才能删除
        m_strRecv.Delete(0, i+1);                               //尾部已接收完整(尾部为/r/n结束),删除不完整的部分
    }

    //截取完整的NMEA-0183输出语句(m_strRecv中可能有多条语句,每条间以/r/n分隔)
    for (;;)
    {
        i = m_strRecv.Find('/n', 0);
        if (i == -1)
            break;                                              //所有的完整输出语句都已经处理完毕,退出循环

        //截取完整的NMEA-0183输出语句
        strNEMA = m_strRecv.Left(i+1);                         
        m_strRecv.Delete(0, i+1);
        //下面对各种输出语句进行分别处理
        if (strNEMA.Find("$GPRMC",0) == 0)
        {
            //该输出语句中的各项以','分隔
            for (i=j=0; strNEMA[i]!='/r'; i++)                  //j为逗号的计数器
            {
                if (strNEMA[i] == ',')
                {
                    j++;
                    str = "";
                    for (i++; strNEMA[i]!=','&&strNEMA[i]!='/r'; i++)
                        str += strNEMA[i];                      //str为某项的值
                    i--;

                    //对各项数据分别处理
                    switch (j)
                    {
                    case 1:                                     //时间(UTC)                  
                        m_strTime = str.Left(6);
                        m_strTime.Insert(2, ':');
                        m_strTime.Insert(5, ':');
                        break;
                    case 2:                                     //状态(A-数据有效;V-数据无效,还未定位)
                        if (str == "A")
                            m_strStatus = "有效数据";
                        else if(str == "V")
                            m_strStatus = "正在定位...";
                        else
                            m_strStatus = "非法数据格式";
                        break;
                    case 3:                                     //纬度(ddmm.mmmm)
                        str.Insert(2, "度");
                        str += "分";
                        m_strLatitude = str;
                        break;
                    case 4:                                     //纬度指示(N-北纬;S-南纬)
                        if (str == "N")
                            m_strLatitude.Insert(0, "北纬");
                        else
                            m_strLatitude.Insert(0, "南纬");
                        break;
                    case 5:                                     //经度(dddmm.mmmm)
                        str.Insert(3, "度");
                        str += "分";
                        m_strLongitude = str;
                        break;
                    case 6:                                     //经度指示(E-东经;W-西经)
                        if (str == "E")
                            m_strLongitude.Insert(0, "东经");
                        else
                            m_strLongitude.Insert(0, "西经");
                        break;
                    case 7:                                     //速度(单位:节)
                        m_strSpeed = str;
                        break;
                    case 8:                                     //航向(单位:度)
                        m_strCourse = str;
                        break;
                    case 9:                                     //日期(UTC)
                        m_strDate = "";
                        m_strDate += "20";
                        m_strDate += str[4];
                        m_strDate += str[5];
                        m_strDate += "-";
                        m_strDate += str[2];
                        m_strDate += str[3];
                        m_strDate += "-";
                        m_strDate += str[0];
                        m_strDate += str[1];
                        break;
                    default:
                        break;
                    }
                }
            }
        }
        else if (strNEMA.Find("$GPGGA",0) == 0)
        {

        }
        else if (strNEMA.Find("$GPGSA",0) == 0)
        {

        }
        else if (strNEMA.Find("$GPGSV",0) == 0)
        {

        }
        else if (strNEMA.Find("$GPGLL",0) == 0)
        {

        }
        else if (strNEMA.Find("$GPVTG",0) == 0)
        {

        }
        else
            return FALSE;                                       //格式错误
    }
    return TRUE;
}

原创粉丝点击