NMEA1803协议3.0版本及以上说明

一、 NMEA1803协议格式:

在这里插入图片描述

二、标准信息发送器标识说明:

1.GNSS7种卫星系统说明

GNSS接收系统主要有以下7个发送系统:

1.Galileo(欧洲伽利略系统)
2.BeiDou(中国北斗系统)
3.GPS(美国GPS系统)
4.QZSS(准天顶系统)
5.NAVIC(IRNSS印度区域导航卫星系统)
6.GLONASS(俄罗斯格洛纳斯系统)
7.Combination of Multiple Satellite Systems(联合卫星系统(以上全部或部分系统联合定位时输出的数据))。

其中:
全球定位系统:
BeiDou、GPS、GLONASS、Galileo
区域定位系统:
QZSS、IRNSS
增强系统:
WAAS(美国)、MSAS(日本)、EGNOS(联合国)、GAGAN(印度)、 NIGCOMSAT-1(尼日亚非洲区域卫星)。

2.NMEA1803协议3.01与4.1版本标识符区别

在这里插入图片描述

三、常用标准信息语句说明:

1.RMC(推荐最小定位信息)

在这里插入图片描述

示例报文:
NMEA:3.0-4.0x示例数据
$GPRMC,121252.000,A,3958.3032,N,11629.6046,E,15.15,359.95,070306,,,A*54
示例报文:
NMEA:4.1x示例数据
$GNRMC,015107.00,A,3412.76124010,N,10849.67444051,E,0.003,114.8,010323,3.4,W,A,V*4C
版本区别:4.1x增加<14>导航状态标识字段

在这里插入图片描述

如上图两个语句使用“x”进行补齐操作
在末尾高亮处(14或13)个字段的位置当“A”对齐后3.0版本直接接替*输出校验位。
而4.1版本“A”对齐后接“,”输出“V”第14字段导航状态标识后接校验位

2.GGA(接收机时间、 位置及定位相关的数据)

请添加图片描述

示例报文:
NMEA:3.0-4.0x示例数据
$GPGGA,121252.000,3937.3032,N,11611.6046,E,1,05,2.0,45.9,M,-5.7,M,,0000*77 
示例报文:
NMEA:4.1x示例数据
$GNGGA,015111.00,3412.76134514,N,10849.67440085,E,1,28,0.6,399.1382,M,-33.6324,M,,*55
版本区别:无区别字段长度相同

在这里插入图片描述

如上图两个语句使用“x”进行补齐操作
字段个数一致二版本下无区别

3.GSA(定位的卫星编号与 DOP 信息)

在这里插入图片描述

示例报文:
NMEA:3.0-4.0x示例数据
$GNGSA,M,3,87,66,77,67,76,,,,,,,,0.7,0.4,0.6*2A
$GNGSA,M,3,02,03,07,25,30,36,08,,,,,,0.7,0.4,0.6*2A
$GNGSA,M,3,01,02,06,07,10,16,24,26,03,33,14,35,0.7,0.4,0.6*23
$GNGSA,M,3,39,40,42,45,59,60,44,04,05,21,09,,0.7,0.4,0.6*2D
示例报文:
NMEA:4.1x示例数据
$GNGSA,A,3,03,04,08,16,27,28,29,31,195,199,,,1.13,0.61,0.94,1*0A
$GNGSA,A,3,,,,,,,,,,,,,1.13,.61,0.94,3*0A
$GNGSA,A,3,03,04,07,10,21,22,26,29,39,40,45,59,1.13,0.61,0.94,4*03
版本区别:4.1x 增加systemid

在这里插入图片描述

如上图两个语句使用“x”或者“,x”进行补齐操作后可以对比法线缺少补全“,x”内容,即缺少systemid内容。
0.6x,x2D(3.0)
0.94,4
03(4.1)

4.GSV(可见卫星的卫星编号及其仰角、 方位角、 载噪比等信息)

在这里插入图片描述

示例报文:
NMEA:3.0-4.0x示例数据
$GPGSV,3,1,10,18,84,067,23,09,67,067,27,22,49,312,28,15,47,231,30*70 
$GPGSV,3,2,10,21,32,199,23,14,25,272,24,05,21,140,32,26,14,070,20*7E 
$GPGSV,3,3,10,29,07,074,,30,07,163,28*7D 
示例报文:
NMEA:4.1x示例数据
$GPGSV,2,1,07,04,33,316,37,03,31,260,44,16,63,232,45,26,72,026,45,1*65
$GPGSV,2,2,07,28,34,075,45,29,12,042,35,31,41,067,40,1*57
版本区别:4.1x增加<14>导航状态标识字段

在这里插入图片描述

如上图在GSV最后进行补齐,可以看到3.0版本缺少“,x”内容,即缺少GNSS信号ID
30*,x70
45,1*65

三、 解析程序参考思路:

这里只提供两种参考方法及部分核心代码段。
分析特点:
NMEA报文格式特点即从“ ”出发每经过一个“,”或末尾“ ∗ ”该数据内容结束, ∗ 之后为校验位。所有标识符 ( “ ”出发每经过一个“,”或末尾“*”该数据内容结束,*之后为校验位。 所有标识符(“ 出发每经过一个或末尾该数据内容结束,之后为校验位。所有标识符(”、“,”、“*”)综和等于字段个数。

方法1:
采用两个指针一前一后进行遍历标识符截取字段。
适用于简单适用,只提取使用或常用的字段,写法简单清晰,可增加标识符个数检索接口,
从而再for中确定每个字段对应len长度。
缺点:编写起来比较冗余,在GSV,GSA等多条解析字段下将变得鸡肋。

void gnssscr_Ctrl(char *gnssscr)
{
    char *strfirst=NULL;
    char *strnext=NUNLL;
    strfirst=gnssscr;
    if((strfirst=strstr(strfirst,"$GNRMC"))!=NULL)
    {
        for(uint8_t i=0;i<14;i++)//RMC 14个段(掐头去尾去中间数据段)
        {
            if (i == 0)
            { 
                if ((strfirst = strstr(gnssscr, ",")) == NULL) 
                {
                    LOG("err");   //解析错误
                }
            }
            else//如果没有错误
            {
               strfirst++;
                strnext = strstr(strfirst, ",");
                LOG("strnext=%s",strnext);          
                if (strnext != NULL)
                {   
                    LOG("strfirst = %s",strfirst);
                    len=strnext-strfirst;
                    switch(i)
                    {
                       case 1: //获取UTC时间
                             memset(gnss_data_src.UTCTime,0,sizeof(gnss_data_src.UTCTime));
                             if(len==0)
                             {
                                 memset(gnss_data_src.UTCTime,0x30,10);
                             }
                             else
                             {
                                 memset(gnss_data_src.UTCTime,0x30,sizeof(gnss_data_src.UTCTime)-1);
                                 memcpy(gnss_data_src.UTCTime+sizeof(gnss_data_src.UTCTime)-len-1,strfirst,len); 
                                 LOG("UTCTimelen:%d--%d",sizeof(gnss_data_src.UTCTime),len); 
                             }
                        break;
                        ....//省略
                    }    
                    strfirst = strnext;//每执行一次subString的值将被替换为subStringNext
                } 
                else
                {
                    LOG("解析错误");
                }  
            }
        }
    }
    if((strfirst=strstr(strfirst,"$GNGGA"))!=NULL)
    {
        for(uint8_t i=0;i<11;i++)//GGA(实际17个段) 11个段(舍弃不需要的段)
        {
            if (i == 0)
            { 
                if ((strfirst = strstr(gnssscr, ",")) == NULL) 
                {
                    LOG("err");   //解析错误
                }
            }
            else//如果没有错误
            {
                strfirst++;
                strnext = strstr(strfirst, ",");
                LOG("strnext=%s",strnext);          
                if (strnext != NULL)
                {   
                    LOG("strfirst = %s",strfirst);
                    len=strnext-strfirst;
                    switch(i)
                    {
                        case 6: //获取定位状态
                              memset(gnss_data_src.Positioning_state,0,sizeof(gnss_data_src.Positioning_state));
                              if(len==0)
                              {
                                  memset(gnss_data_src.Positioning_state,0x30,1);
                              }
                              else
                              {                                           
                                  memset(gnss_data_src.Positioning_state,0x30,sizeof(gnss_data_src.Positioning_state)-1);
                                  memcpy(gnss_data_src.Positioning_state+sizeof(gnss_data_src.Positioning_state)-len-1,strfirst,len);
                              }
                              LOG("gnss_data_src.Positioning_state:%s",gnss_data_src.Positioning_state); 
                        break;   
                        ...//省略
                    }
                    strfirst = strnext;//每执行一次subString的值将被替换为subStringNext
                } 
                else
                {
                    LOG("解析错误");
                }  
            }
        }
    }
}

方法2:
遍历标识符个数,将所有标识符地址保存在一个指针数组中。两个相邻的指针相减即为该字段数据内容的长度。
方法说明:
依照核心接口,可自由开发后续解析,适应GSV或者GSA等多条内容的解析。并可兼容因3.0或4.1版本字段总个数不一的情况。

/**************************************
 * 函数名称:get_Symbol_Sign
 * 函数入口:
 *                      gnsscr:定位源串
 *                      delimt:索引
 * 函数出口:
 *                      p:索引值地址
 *                      正确返回已经存储的地址个数
 *                      错误返回0;
 * 函数说明:遍历存储所有","的地址
 * ***************************************/
uint8_t get_Symbol_Sign(const char *gnssscr, char *p[],const char *delimt)
{
    uint8_t count= 0;
    char *scr = NULL;
    if ((scr = strstr(gnssscr, delimt)) != NULL)
    {
        p[count++] = scr; // 存首地址"$"
        while (1)
        {
            if (*scr < ' ' || *scr > 'z') // 存在非法字符将结束
            {
                LOG("Illegal character exists!!!\r\n");
                return count; 
            }
            else
            {
                if (*scr == ',')
                {
                    p[count++] = scr; // 存“,”地址
                    
                }
                else if(*scr == '*')
                {
                    LOG("count:%d\r\n", count);
                    p[count++] = scr; // 存“*”地址
                    return count;
                }
            }
            scr++;
        }
    }
    else
    {
        return 0;
    }
    return count;
}
/**************************************
 * 函数名称:gnss_Addr_Ctrl
 * 函数入口:
 *                      gnsscr:定位源串
 *                      p: “,”及“*”位置存放缓冲区
 *                      len  :报文段的个数
 *                      buf:数据段缓冲区
 * 函数出口:
 * 函数说明:检索“,”“*”前每段数据存放buf
* 二维数组元素大小给定20,因为最长数据段长度如假设经度小数点前8位,小数点后8位加点1位 加\0 才18字节
 * ***************************************/
void gnss_Addr_Ctrl(const char *gnssscr, char *p[], uint8_t segcont, char (*buf)[20])
{
    uint8_t i=0;
    uint16_t scrlen = 0;
    memcpy(*buf, gnssscr, p[1] - p[0]);//先拿每个语句的头如$GNRMC
    scrlen += strlen(*buf) + 1;
    for(i = 1; i < segcont - 1; i++)
    {
        memcpy(*(buf + i), gnssscr + scrlen, p[i + 1] - p[i]-1 );//从第一个拿到除校验位最后一个
        //LOG("buf:%s\r\n",*(buf + i));
        scrlen += strlen(*(buf + i)) + 1;
    }
    memcpy(*(buf + i), gnssscr + scrlen, 2 );//最后拿校验位
}
/**************************************
 * 函数名称:get_Head_Kind
 * 函数入口:
 *                          gnssscr:定位数据原串
 *                         
 *                          head:头存储二维数组
 *          
 *                          delim:索引标识符
 * 函数出口:
 *                      head:检索同一数据(如xxGGA)头种类存储二维组
 *                      返回检索相关头的个数
 * 函数说明: 获取语句头的种类,如定位板卡吐出数据中有GNGGA与GPGGA
*或存储这两种系统头,用于其他函数检索,可将解析后的数据解析到对应系统结构体中
 * ***************************************/
uint8_t get_Head_Kind(const char *gnssscr,char (*head)[7],const char *delimt)
{
     uint8_t num=0;
     uint8_t cont=0;
     uint8_t diff_flag=0;
     char *headbuf=NULL;
     if((headbuf=strstr(gnssscr,delimt))!=NULL)
     {
            memcpy(head[cont],headbuf-3,6);// 第一出现只存
            while(1)
            { 
                 if((headbuf=strstr(headbuf+1,delimt))!=NULL)
                 {
                        for(num=0;num<cont+1;num++)
                        {
                                if(!memcmp(headbuf-3,head[num],6))
                                {
                                    diff_flag=1;
                                    break;
                                }
                        }
                        if(!diff_flag)
                        {
                                memcpy(head[++cont],headbuf-3,6);// 第一出现只存  
                        }
                        diff_flag=0;
                 }
                 else
                 {
                     return cont+1;
                 }
            }
     }
     else
     {
        return cont;
     }
}
/**************************************
 * 函数名称:get_Nmea1803_Ver
 * 函数入口:
 *              head:GNSS头二维数组

 *              len:头个数
 * 函数出口:
 *              返回1:NEMA4.10
 *              返回0:NEMA 3.0-4.0
 * 函数说明:获取当前NMEA的版本号
 * ***************************************/
uint8_t get_Nmea1803_Ver(char (*head)[7],uint8_t len,const char *gnssscr)
{
    char *p[25] = {0};//存储每个“,”地址
    char *strfirst=NULL;
    char rmchead[7]={0};
    for(uint8_t i=0;i<len;i++)//不完全,需要增加一些字段段个数测验
    {
        if((!memcmp(head[i]+1,"GB",2) ) || (!memcmp(head[i]+1,"GQ",2)) || (!memcmp(head[i]+1,"GI",2)))//4.1
        {
            LOG("NMEA_1803 4.1UP!!!\r\n");
            return 1;
        }
    }
    if((strfirst=strstr(gnssscr,"RMC"))!=NULL)//极端下可以再次增加其他相关字段校验判断当前默认判断RMC
    {
        memcpy(rmchead,strfirst-3,6);
        if (get_Symbol_Sign(gnssscr, p, rmchead)==15)//其他判断方法
        {
           LOG("NMEA_1803 4.1UP!!!\r\n");
            return 1;//4.1
        }
    }
    LOG("NMEA_1803 3.0UP!!!\r\n");
    return 0;//3.0
}
/**************************************
 * 函数名称:head_to_select_num
 * 函数入口:
 *                          head:定位数据原串

 *                          verflag:版本标识
 * 函数出口:
 *                      根据返回的版本标识确定调用结构体
 * 函数说明:解析GNSS数据到结构体
 * ***************************************/
uint8_t head_to_select_num(char *head,uint8_t verflag)
{
    QL_UART_DEMO_LOG("head%s\r\n",head);
    if ((!memcmp(head,"GP",2)))// 公共资源 GPs
    {
        return 0;
    }
    else if((!memcmp(head,"GN",2)))//Combination of Multiple Satellite Systems
    {
        return 1;
    }
    else if((!memcmp(head,"GA",2)))//Galileo
    {
        return 4;
    }
    else if((!memcmp(head,"GL",2)))//GLONASS
    {
        return 3;
    }
    if(verflag==1)//4.11专精
     {
        if((!memcmp(head,"GB",2)))//BeiDou
        {
                return 2;
        }
        else  if((!memcmp(head,"GQ",2)))//膏药旗Qzss
        {
                    return 6;
        }
        else  if((!memcmp(head,"GI",2)))//NAVIC(IRNSS)
        {
                    return 5;
        }
     }
     else//3.0
     {
        if((!memcpy(head,"BD",2)))//BeiDou
        {
                return 2;
        }
        else  if((!memcpy(head,"IR",2)))//NAVIC(IRNSS)
        {
                    return 5;
        }
     }
    return 7;
}

下面展示 解析RMC时代码段

/**************************************
 * 函数名称:gnss_Class_RMC
 * 函数入口:
 *                          gnssscr:定位数据原串
 * 函数出口:
 * 函数说明:RMC数据解析 
 * ***************************************/
void gnss_Class_RMC(const char *gnssscr)
{
    char headbuf[7][7]={0};//不管什么协议最多上限只会存在七种头
    uint8_t i=0;
    uint8_t len=0;
    uint8_t verflag=0;
    uint8_t select_num=0;
    if((len=get_Head_Kind(gnssscr,headbuf,"RMC")))//判断种类
    {
        LOG("len:%d\r\n",len);
         verflag=get_Nmea1803_Ver(headbuf, len,gnssscr);//判断版本号
        LOG("verflag:%d\r\n",verflag);
        for(i=0;i<len;i++)
        {
            select_num=head_to_select_num(&headbuf[i][1], verflag);//头转系统号
            LOG("select_num:%d\r\n",select_num);
            switch(select_num)//走那个结构体            {
                case GNSS_Gpgs:
                             gnss_RMC_Crtl(gnssscr,&headbuf[i][0],&gnss_rmc_gp);//GPS系统解析
                break;
                case GNSS_Comsys:
                             gnss_RMC_Crtl(gnssscr,&headbuf[i][0],&gnss_rmc_gn);//GN联合系统解析
                break;
                case  GNSS_Beidou:
                             gnss_RMC_Crtl(gnssscr,&headbuf[i][0],&gnss_rmc_bd);//北斗系统解析
                break;
                case  GNSS_Glonass:break;//暂未开发
                case  GNSS_Galileo:break;
                case  GNSS_Navic:break;
                case  GNSS_Qzss:break;
                default:break;
            }
        }
    }
    else
    {
            LOG("RMC not have\r\n");
    }
}
/**************************************
 * 函数名称:gnss_RMC_Crtl
 * 函数入口:
 *                          gnssscr:定位数据原串
 *
 *                          headbuf:GNSS头标识符
 *                          
 *                          gnssdatastruct:参数结构体
 * 函数出口:
 *                      head:检索头种类存储二维组
 *                      返回检索相关头的个数
 * 函数说明:解析GNSS数据到结构体
 * ***************************************/
uint8_t gnss_RMC_Crtl(const char *gnssscr,const char *headbuf,GnssRMCData * gnss_rmc_struct)
{
    uint8_t len=0;
    char *p[25] = {0};//存储每个“,”地址
    char *firststr = NULL;//中间指针
    if ((len = get_Symbol_Sign(gnssscr, p, headbuf)))//遍历标识附地址存储
    {

        firststr = strstr(gnssscr, headbuf);
        get_Rmc_Data(firststr, len, p, gnss_rmc_struct);//按照存储标识地址获取数据到结构体变量
        return 0;
    }
    else
    {
        QL_UART_DEMO_LOG("[%s]not have\r\n",headbuf);
        return 1;
    }
}
/**************************************
* 函数名称:get_Rmc_Data
* 函数入口:
*                    gnsscr:定位源串
*                     len “,”个数
*                    p: “,”位置存放缓冲区
*                    gnssstruct:存数据放缓冲区
* 函数出口:
* 函数说明:检索gnssscr中“,”位置
* ***************************************/
    void get_Rmc_Data(char *gnssscr, uint8_t len, char *p[], GnssRMCData *gnsss_rmc_struct)
    {
        char buf[15][20] = {0};
        uint8_t i = 0;
        gnss_Addr_Ctrl(gnssscr, p, len, buf);//根据标识符地址解析各段数据到BUF中
        #if DATALOGSWITCH//可屏蔽LOG
        for (i = 0; i < len; i++)//len即是标识符个数
        {
            LOG("buf:%s\r\n", buf[i]);
        }
        #endif
        for(i=0;i<len;i++)
        {
            switch(i)
            {
                case 0:
                        memcpy(gnsss_rmc_struct->head, buf[i], strlen(buf[i])+1);//语句头
                        //LOG("RMChead:%s\r\n", gnsss_rmc_struct->head);
                break;// 
                case 1:
                        memcpy(gnsss_rmc_struct->UTCtime, buf[i], strlen(buf[i])+1);//UTCTIME
                        //LOG("UTCTime:%s\r\n", gnsss_rmc_struct->UTCtime);
                break;
                case 2:
                        memcpy(gnsss_rmc_struct->status, buf[i], strlen(buf[i])+1);//定位状态
                        //LOG("UTCTime:%s\r\n", gnsss_rmc_struct->status);
                break;
                case 3:
                        memcpy(gnsss_rmc_struct->lat, buf[i], strlen(buf[i])+1);//纬度
                        //LOG("Latitude:%s\r\n", gnsss_rmc_struct->lat);
                break;
                case 4:
                        memcpy(gnsss_rmc_struct->uLat, buf[i], strlen(buf[i])+1);//纬度标识
                        //LOG("N_S:%s\r\n", gnsss_rmc_struct->uLat);
                break;
                case 5:
                        memcpy(gnsss_rmc_struct->lon, buf[i], strlen(buf[i])+1);//经度
                        //LOG("Longitude:%s\r\n", gnsss_rmc_struct->lon);
                break;
                case 6:
                        memcpy(gnsss_rmc_struct->uLon, buf[i], strlen(buf[i])+1);//经度标识
                        //LOG("E_W:%s\r\n", gnsss_rmc_struct->uLon);
                break;
                case 7:
                        memcpy(gnsss_rmc_struct->spd, buf[i], strlen(buf[i])+1);//速度
                        //LOG("Speed:%s\r\n", gnsss_rmc_struct->spd);
                break;
                case 8:
                        memcpy(gnsss_rmc_struct->cog, buf[i], strlen(buf[i])+1);//航向角
                        //LOG("Azimuth:%s\r\n", gnsss_rmc_struct->cog);
                break;
                case 9:
                        memcpy(gnsss_rmc_struct->date, buf[i], strlen(buf[i])+1);//日期
                        //LOG("UTCDate:%s\r\n", gnsss_rmc_struct->date);
                break;
                case 10:
                        memcpy(gnsss_rmc_struct->mv, buf[i], strlen(buf[i])+1);//磁偏角
                        //LOG("mv:%s\r\n", gnsss_rmc_struct->mv);
                break;
                case 11:
                        memcpy(gnsss_rmc_struct->mvE, buf[i], strlen(buf[i])+1);磁偏角方向
                        //LOG("mvE:%s\r\n", gnsss_rmc_struct->mvE);
                break;
                case 12:
                        memcpy(gnsss_rmc_struct->mode, buf[i], strlen(buf[i])+1);//定位模式
                        //LOG("rmc_mode:%s\r\n", gnsss_rmc_struct->mode);
                break;
                case 13:
                case 14:
                        if(len==15)//因为第15字段因版本原因可能存在后着不存在
                        {
                            memcpy(gnsss_rmc_struct->xor, buf[i+1], strlen(buf[i])+1);//异或校验
                            //LOG("XOR:%s\r\n", gnsss_rmc_struct->xor);
                        }
                        else if(len==14)
                        {
                            break;
                        }
                break;
                default:break;
            }
        }
    }

GGA解析与RMC类似

GSA: 解析方法类似,但是当需要进行单系统解析操作时需要做特殊处理,例如在NMEA4.0版本下由于没有systemid字段,因此在输出$GNGSA联合系统也无法进行区分定位系统,但是在NMEA4.1以上系统将需要进行判断systemid从而将联合系统解析到单系统。(当处理NMEA4.0以下版本数据时,一般遍历所有的GNGSA相比较,选最长的一个报文进行解析即可,毕竟GSA中最关心的数据是三个定位因子,此处暂不提供代码)。

GSV: 由于具备总条数、当前条数、卫星数且已经知道每条语句最多显示4组卫星参数不足将在下一条。也就是说当总条目为5,那么前4条是4组卫星参数段,最后一条GSV语句需要结合卫星总数来计算剩余卫星总数,例如卫星数为18,那么前4条GSV已经表达了4*4=16组卫星参数了,因此最后一条GSV语句将只显示18-16=2组卫星参数。需要注意GSV在不存在其他卫星组时将会缺省,卫星组内数据段与其他报文内容无数据“,”间将会补空,例如上例GSV语句最后只有2组卫星数,将只显示该两组数据加校验位结束,如下例子

$GPGSV,3,1,10,18,84,067,23,09,67,067,27,22,49,312,28,15,47,231,30*70 
$GPGSV,3,2,10,21,32,199,23,14,25,272,24,05,21,140,32,26,14,070,20*7E 
$GPGSV,3,3,10,29,07,074,,30,07,163,28*7D 

从而在最后一条处理的时候需要做些处理及注意。

总结:以上解析内容完全依照get_Symbol_Sign(保存标识符地址API)与gnss_Addr_Ctrl(解析标识符间内容API),来完成后续操作的,可自由依照次进行开发及升级。以上仅提供交流学习。如有不正确欢迎批评指正。

  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值