nmealib代碼分析

從之前的samples/parse/main.c開始。

以其中的一條GPGGA語句爲例。

nmeaINFO結構彙總的是gps數據信息,裏面包括utc時間、定位狀態、質量因子、經緯度、速度、方向等信息,之所以說是彙總,那是因爲這裏是對所有的nmea語句進行解析,然後將相應的數據賦值到該結構中,而不僅僅是其中的一條nmea語句,因爲一條nmea語句不可能包括所有的gps信息。

nmeaPARSER是解析nmea所需要的一個結構。

然後是nmea_zero_INFO。
void nmea_zero_INFO(nmeaINFO *info)
{
    memset(info, 0, sizeof(nmeaINFO));
    nmea_time_now(&info->utc);
    info->sig = NMEA_SIG_BAD;
    info->fix = NMEA_FIX_BAD;
}

這裏是對nmeaINFO這個結構中數據進行清零操作,使用nmea_time_now函數對其中utc時間賦一個初值,初值就是當前的系統時間,如果沒有從nmea中解析出時間信息,那麼最後的結果就是你當前的系統時間。而nmeaINFO中的sig、fix分別是定位狀態和定位類型。

緊接着是nmea_parser_init。

int nmea_parser_init(nmeaPARSER *parser)
{
    int resv = 0;
    int buff_size = nmea_property()->parse_buff_size;

    NMEA_ASSERT(parser);

    if(buff_size < NMEA_MIN_PARSEBUFF)
        buff_size = NMEA_MIN_PARSEBUFF;

    memset(parser, 0, sizeof(nmeaPARSER));

    if(0 == (parser->buffer = malloc(buff_size)))
        nmea_error("Insufficient memory!");
    else
    {
        parser->buff_size = buff_size;
        resv = 1;
    }    

    return resv;
}
這個函數自然是對nmeaPARSER結構做初始化,首先是buff_size,這裏值爲NMEA_DEF_PARSEBUFF,即1024。然後爲buffer分配內存,這裏自然是分配的1024字節大小。

最後調用nmea_parse函數對nmea語句進行解析。
int nmea_parse(    
    nmeaPARSER *parser,
    const char *buff, int buff_sz,
    nmeaINFO *info
    )
{
    int ptype, nread = 0;
    void *pack = 0;

    NMEA_ASSERT(parser && parser->buffer);

    nmea_parser_push(parser, buff, buff_sz);

    while(GPNON != (ptype = nmea_parser_pop(parser, &pack)))
    {
        nread++;

        switch(ptype)
        {
        case GPGGA:
            nmea_GPGGA2info((nmeaGPGGA *)pack, info);
            break;
        case GPGSA:
            nmea_GPGSA2info((nmeaGPGSA *)pack, info);
            break;
        case GPGSV:
            nmea_GPGSV2info((nmeaGPGSV *)pack, info);
            break;
        case GPRMC:
            nmea_GPRMC2info((nmeaGPRMC *)pack, info);
            break;
        case GPVTG:
            nmea_GPVTG2info((nmeaGPVTG *)pack, info);
            break;
        };

        free(pack);
    }

    return nread;
}
這個函數有四個參數,分別是nmeaPARSER指針,buff對應需要解析的nmea語句,buff_sz爲nmea語句的長度,nmeaINFO指針。

調用nmea_parser_push函數。
int nmea_parser_push(nmeaPARSER *parser, const char *buff, int buff_sz)
{
    int nparse, nparsed = 0;

    do
    {
        if(buff_sz > parser->buff_size)
            nparse = parser->buff_size;
        else
            nparse = buff_sz;

        nparsed += nmea_parser_real_push(
            parser, buff, nparse);

        buff_sz -= nparse;

    } while(buff_sz);

    return nparsed;
}

在do while裏又調用了nmea_parser_real_push函數,這裏nparse還是等於buff_sz大小。
int nmea_parser_real_push(nmeaPARSER *parser, const char *buff, int buff_sz)
{
    int nparsed = 0, crc, sen_sz, ptype;
    nmeaParserNODE *node = 0;

    NMEA_ASSERT(parser && parser->buffer);

    /* clear unuse buffer (for debug) */
    /*
    memset(
        parser->buffer + parser->buff_use, 0,
        parser->buff_size - parser->buff_use
        );
        */

    /* add */
    if(parser->buff_use + buff_sz >= parser->buff_size)
        nmea_parser_buff_clear(parser);

    memcpy(parser->buffer + parser->buff_use, buff, buff_sz);
    parser->buff_use += buff_sz;

    /* parse */
    for(;;node = 0)
    {
        sen_sz = nmea_find_tail(
            (const char *)parser->buffer + nparsed,
            (int)parser->buff_use - nparsed, &crc);

        if(!sen_sz)
        {
            if(nparsed)
                memcpy(
                parser->buffer,
                parser->buffer + nparsed,
                parser->buff_use -= nparsed);
            break;
        }
        else if(crc >= 0)
        {
            ptype = nmea_pack_type(
                (const char *)parser->buffer + nparsed + 1,
                parser->buff_use - nparsed - 1);

            if(0 == (node = malloc(sizeof(nmeaParserNODE))))
                goto mem_fail;

            node->pack = 0;

            switch(ptype)
            {
            case GPGGA:
                if(0 == (node->pack = malloc(sizeof(nmeaGPGGA))))
                    goto mem_fail;
                node->packType = GPGGA;
                if(!nmea_parse_GPGGA(
                    (const char *)parser->buffer + nparsed,
                    sen_sz, (nmeaGPGGA *)node->pack))
                {
                    free(node);
                    node = 0;
                }
                break;
            case GPGSA:
                if(0 == (node->pack = malloc(sizeof(nmeaGPGSA))))
                    goto mem_fail;
                node->packType = GPGSA;
                if(!nmea_parse_GPGSA(
                    (const char *)parser->buffer + nparsed,
                    sen_sz, (nmeaGPGSA *)node->pack))
                {
                    free(node);
                    node = 0;
                }
                break;
            case GPGSV:
                if(0 == (node->pack = malloc(sizeof(nmeaGPGSV))))
                    goto mem_fail;
                node->packType = GPGSV;
                if(!nmea_parse_GPGSV(
                    (const char *)parser->buffer + nparsed,
                    sen_sz, (nmeaGPGSV *)node->pack))
                {
                    free(node);
                    node = 0;
                }
                break;
            case GPRMC:
                if(0 == (node->pack = malloc(sizeof(nmeaGPRMC))))
                    goto mem_fail;
                node->packType = GPRMC;
                if(!nmea_parse_GPRMC(
                    (const char *)parser->buffer + nparsed,
                    sen_sz, (nmeaGPRMC *)node->pack))
                {
                    free(node);
                    node = 0;
                }
                break;
            case GPVTG:
                if(0 == (node->pack = malloc(sizeof(nmeaGPVTG))))
                    goto mem_fail;
                node->packType = GPVTG;
                if(!nmea_parse_GPVTG(
                    (const char *)parser->buffer + nparsed,
                    sen_sz, (nmeaGPVTG *)node->pack))
                {
                    free(node);
                    node = 0;
                }
                break;
            default:
                free(node);
                node = 0;
                break;
            };

            if(node)
            {
                if(parser->end_node)
                    ((nmeaParserNODE *)parser->end_node)->next_node = node;
                parser->end_node = node;
                if(!parser->top_node)
                    parser->top_node = node;
                node->next_node = 0;
            }
        }

        nparsed += sen_sz;
    }

    return nparsed;

mem_fail:
    if(node)
        free(node);

    nmea_error("Insufficient memory!");

    return -1;
}
首先將要解析的nmea字符串拷貝到nmeaPARSER的buffer指針處,注意這裏最開始就分配好了1024字節大小的內存空間,然後對nmeaPARSER的buff_use做一個賦值操作,這裏賦值爲nmea語句的長度值。

到了for循環中,首先調用的是nmea_find_tail函數。
int nmea_find_tail(const char *buff, int buff_sz, int *res_crc)
{
    static const int tail_sz = 3 /* *[CRC] */ + 2 /* \r\n */;

    const char *end_buff = buff + buff_sz;
    int nread = 0;
    int crc = 0;

    NMEA_ASSERT(buff && res_crc);

    *res_crc = -1;

    for(;buff < end_buff; ++buff, ++nread)
    {
        if(('$' == *buff) && nread)
        {
            buff = 0;
            break;
        }
        else if('*' == *buff)
        {
            if(buff + tail_sz <= end_buff && '\r' == buff[3] && '\n' == buff[4])
            {
                *res_crc = nmea_atoi(buff + 1, 2, 16);
                nread = buff_sz - (int)(end_buff - (buff + tail_sz));
                if(*res_crc != crc)
                {
                    *res_crc = -1;
                    buff = 0;
                }
            }

            break;
        }
        else if(nread)
            crc ^= (int)*buff;
    }

    if(*res_crc < 0 && buff)
        nread = 0;

    return nread;
}
這個函數主要幹什麼的呢,主要是找到nmea語句的結束符"\r\n",並判斷其crc值是否正確,如果你私自改了nmea語句中的某個值,而又沒有修改crc值,那麼這裏解析是不會成功的。

如果在其他地方發現了nmea語句的起始符"$",那麼證明這條nmea語句是有問題的,直接退出。

那麼邊計算crc值,邊找nmea語句的結束符,如果找到了一個符號"*",那麼結束符就在後面的第3、第4個位置處。這裏一併將nmea語句中的crc值取出來,並和前面計算的crc值做一個比較,如果不想等,說明這條nmea語句有問題,直接丟棄。最後返回的nread還是nmea語句的長度值。


返回到nmea_parser_real_push函數中,sen_sz不爲0,那麼自然走下面的else if流程。

然後調用nmea_pack_type函數判斷nmea語句的包類型。
int nmea_pack_type(const char *buff, int buff_sz)
{
    static const char *pheads[] = {
        "GPGGA",
        "GPGSA",
        "GPGSV",
        "GPRMC",
        "GPVTG",
    };

    NMEA_ASSERT(buff);

    if(buff_sz < 5)
        return GPNON;
    else if(0 == memcmp(buff, pheads[0], 5))
        return GPGGA;
    else if(0 == memcmp(buff, pheads[1], 5))
        return GPGSA;
    else if(0 == memcmp(buff, pheads[2], 5))
        return GPGSV;
    else if(0 == memcmp(buff, pheads[3], 5))
        return GPRMC;
    else if(0 == memcmp(buff, pheads[4], 5))
        return GPVTG;

    return GPNON;
}
這裏只支持5種類型的nmea語句,有GPGGA、GPGSA、GPGSV、GPRMC和GPVTG,這裏只需要判斷前5個字符就可以了,返回這個類型值。

如果是GPGGA類型的nmea語句,那自然是調用nmea_parse_GPGGA這個函數對其進行解析了。在這之前首先爲nmeaParserNODE和其中的pack申請了內存空間,那麼自然這裏的解析結果肯定是存儲在pack這裏了。
int nmea_parse_GPGGA(const char *buff, int buff_sz, nmeaGPGGA *pack)
{
    char time_buff[NMEA_TIMEPARSE_BUF];

    NMEA_ASSERT(buff && pack);

    memset(pack, 0, sizeof(nmeaGPGGA));

    nmea_trace_buff(buff, buff_sz);

    if(14 != nmea_scanf(buff, buff_sz,
        "$GPGGA,%s,%f,%C,%f,%C,%d,%d,%f,%f,%C,%f,%C,%f,%d*",
        &(time_buff[0]),
        &(pack->lat), &(pack->ns), &(pack->lon), &(pack->ew),
        &(pack->sig), &(pack->satinuse), &(pack->HDOP), &(pack->elv), &(pack->elv_units),
        &(pack->diff), &(pack->diff_units), &(pack->dgps_age), &(pack->dgps_sid)))
    {
        nmea_error("GPGGA parse error!");
        return 0;
    }

    if(0 != _nmea_parse_time(&time_buff[0], (int)strlen(&time_buff[0]), &(pack->utc)))
    {
        nmea_error("GPGGA time parse error!");
        return 0;
    }

    return 1;
}
這裏最重要的就是nmea_scanf函數了,這裏纔是真正的解析nmea語句的函數,這裏這個函數的名字也很特別,帶了個scanf。

回憶一下c語言中的scanf函數是怎麼用的,例如scanf("%d", &val);,等待我們輸入一個數字之後,那麼最後的結果肯定是存在val中的。

這裏的nmea_scanf也是類似的,只是這裏的數據是在buff裏,數據還是沒有變化,還是那一條nmea語句。

nmea_scanf這個函數大家也可以去細看一下,反正最後的解析結果pack這裏。


還是回到nmea_parser_real_push函數這裏, 最後到了這裏:
if(node)
{
	if(parser->end_node)
		((nmeaParserNODE *)parser->end_node)->next_node = node;
	parser->end_node = node;
	if(!parser->top_node)
		parser->top_node = node;
	node->next_node = 0;
}
node是找到了,初始時end_node、top_node都是都是爲空的,那麼都指向這裏的node。


回到nmea_parse函數這裏。nmea_parser_push函數執行完了,然後是調用nmea_parser_pop函數。
int nmea_parser_pop(nmeaPARSER *parser, void **pack_ptr)
{
    int retval = GPNON;
    nmeaParserNODE *node = (nmeaParserNODE *)parser->top_node;

    NMEA_ASSERT(parser && parser->buffer);

    if(node)
    {
        *pack_ptr = node->pack;
        retval = node->packType;
        parser->top_node = node->next_node;
        if(!parser->top_node)
            parser->end_node = 0;
        free(node);
    }

    return retval;
}
首先找到之前的pack,獲取他的packType並返回。

如果packType是GPGGA,那麼調用nmea_GPGGA2info,而這裏的pack也強制轉換爲了nmeaGPGGA指針。
void nmea_GPGGA2info(nmeaGPGGA *pack, nmeaINFO *info)
{
    NMEA_ASSERT(pack && info);

    info->utc.hour = pack->utc.hour;
    info->utc.min = pack->utc.min;
    info->utc.sec = pack->utc.sec;
    info->utc.hsec = pack->utc.hsec;
    info->sig = pack->sig;
    info->HDOP = pack->HDOP;
    info->elv = pack->elv;
    info->lat = ((pack->ns == 'N')?pack->lat:-(pack->lat));
    info->lon = ((pack->ew == 'E')?pack->lon:-(pack->lon));
    info->smask |= GPGGA;
}
而這個函數自然是對info中的某些數據做了一些賦值操作,包括經緯度、utc時間等。

最後解析結束。


ubox協議下載:http://download.csdn.net/detail/mcgrady_tracy/9407968

參考:http://www.gpsbaby.com/wz/yl.html
http://www.gpsbaby.com/wz/nmea.html
http://aprs.gids.nl/nmea/
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章