rtklib-RINEX文件读取-rinex.c解析(二)


decode_obsh-函数

注释的 // 没加是为了在代码块中显示更加明显,若直接复制源代码需加之。

static void decode_obsh(FILE *fp, char *buff, double ver, int *tsys,
                        char tobs[][MAXOBSTYPE][4], nav_t *nav, sta_t *sta)
{
    /* default codes for unknown code */
    const char *defcodes[]={
        "CWX    ",  /* GPS: L125____ */
        "CC     ",  /* GLO: L12_____ */
        "X XXXX ",  /* GAL: L1_5678_ */
        "CXXX   ",  /* QZS: L1256___ */
        "C X    ",  /* SBS: L1_5____ */
        "X  XX  ",  /* BDS: L1__67__ */
        "  A   A"   /* IRN: L__5___9 */
    };  定义一个指针数组,不知道的编码直接采取默认字符串加空格的形式存储
    
    double del[3];
    int i,j,k,n,nt,prn,fcn;
    const char *p;
    char *label=buff+60,str[4];
    
    trace(4,"decode_obsh: ver=%.2f\n",ver);
    
    if      (strstr(label,"MARKER NAME"         )) {
        if (sta) setstr(sta->name,buff,60);
    }  label指向第61例,与字符串比较,若相同则将缓冲区第160列复制到结构体sta中的name数组中,表示制造商的名称,else if类同。
    
    else if (strstr(label,"MARKER NUMBER"       )) { /* opt */
        if (sta) setstr(sta->marker,buff,20);
    }
    else if (strstr(label,"MARKER TYPE"         )) ; /* ver.3 */
    else if (strstr(label,"OBSERVER / AGENCY"   )) ;  不做处理空语句
    
    else if (strstr(label,"REC # / TYPE / VERS" )) {
        if (sta) {
            setstr(sta->recsno, buff,   20);
            setstr(sta->rectype,buff+20,20);
            setstr(sta->recver, buff+40,20);
        }  setstr函数在rtklib-RINEX文件读取-rinex.c解析(一)种做过解释,意思是复制第二参数到第三参数之间位置上的所有字符并去除尾部空格,详细可转到(一)
    }
    else if (strstr(label,"ANT # / TYPE"        )) {
        if (sta) {
            setstr(sta->antsno,buff   ,20);
            setstr(sta->antdes,buff+20,20);
        }
    }
    else if (strstr(label,"APPROX POSITION XYZ" )) {
        if (sta) {
            for (i=0,j=0;i<3;i++,j+=14) sta->pos[i]=str2num(buff,j,14);
        } str2num函数将在下面介绍具体细节
    }
    else if (strstr(label,"ANTENNA: DELTA H/E/N")) {
        if (sta) {
            for (i=0,j=0;i<3;i++,j+=14) del[i]=str2num(buff,j,14);
            sta->del[2]=del[0]; /* h */
            sta->del[0]=del[1]; /* e */
            sta->del[1]=del[2]; /* n */
        }
    }
    else if (strstr(label,"ANTENNA: DELTA X/Y/Z")) ; /* opt ver.3 */
    else if (strstr(label,"ANTENNA: PHASECENTER")) ; /* opt ver.3 */
    else if (strstr(label,"ANTENNA: B.SIGHT XYZ")) ; /* opt ver.3 */
    else if (strstr(label,"ANTENNA: ZERODIR AZI")) ; /* opt ver.3 */
    else if (strstr(label,"ANTENNA: ZERODIR XYZ")) ; /* opt ver.3 */
    else if (strstr(label,"CENTER OF MASS: XYZ" )) ; /* opt ver.3 */
    else if (strstr(label,"SYS / # / OBS TYPES" )) { /* ver.3 */
        if (!(p=strchr(syscodes,buff[0]))) {
            trace(2,"invalid system code: sys=%c\n",buff[0]);
            return;
        } 如果观测类型不在static const char syscodes[]="GREJSCI"; 中输出错误消息
        
        i=(int)(p-syscodes);
        n=(int)str2num(buff,3,3);
        for (j=nt=0,k=7;j<n;j++,k+=4) {
            if (k>58) {
                if (!fgets(buff,MAXRNXLEN,fp)) break;
                k=7;
            }
            if (nt<MAXOBSTYPE-1) setstr(tobs[i][nt++],buff+k,3);
        }
        *tobs[i][nt]='\0';  存储观测类型,分星座类型用三维数组进行存储【星座类型】【观测类型】【数量】
        
        /* change beidou B1 code: 3.02 draft -> 3.02/3.03 */
        if (i==5) {
            for (j=0;j<nt;j++) if (tobs[i][j][1]=='2') tobs[i][j][1]='1'; 北斗数据类型转换
        }
        /* if unknown code in ver.3, set default code */
        for (j=0;j<nt;j++) {
            if (tobs[i][j][2]) continue;
            if (!(p=strchr(frqcodes,tobs[i][j][1]))) continue;
            tobs[i][j][2]=defcodes[i][(int)(p-frqcodes)];
            trace(2,"set default for unknown code: sys=%c code=%s\n",buff[0],
                  tobs[i][j]);  如果没有该观测类型,就进行错误提示
        }
    }
    else if (strstr(label,"WAVELENGTH FACT L1/2")) ; /* opt ver.2 */
    else if (strstr(label,"# / TYPES OF OBSERV" )) { /* ver.2 */
        n=(int)str2num(buff,0,6);
        for (i=nt=0,j=10;i<n;i++,j+=6) {
            if (j>58) {
                if (!fgets(buff,MAXRNXLEN,fp)) break;
                j=10;
            } rinex2版本的观测类型存储
            if (nt>=MAXOBSTYPE-1) continue;
            if (ver<=2.99) {
                setstr(str,buff+j,2);
                convcode(ver,SYS_GPS,str,tobs[0][nt]);
                convcode(ver,SYS_GLO,str,tobs[1][nt]);
                convcode(ver,SYS_GAL,str,tobs[2][nt]);
                convcode(ver,SYS_QZS,str,tobs[3][nt]);
                convcode(ver,SYS_SBS,str,tobs[4][nt]);
                convcode(ver,SYS_CMP,str,tobs[5][nt]);
            }
            nt++;
        }  rinex从23版本的转换
        *tobs[0][nt]='\0';  末尾加上空字符
    }
    else if (strstr(label,"SIGNAL STRENGTH UNIT")) ; /* opt ver.3 */
    else if (strstr(label,"INTERVAL"            )) ; /* opt */
    else if (strstr(label,"TIME OF FIRST OBS"   )) {
        if      (!strncmp(buff+48,"GPS",3)) *tsys=TSYS_GPS;
        else if (!strncmp(buff+48,"GLO",3)) *tsys=TSYS_UTC;
        else if (!strncmp(buff+48,"GAL",3)) *tsys=TSYS_GAL;
        else if (!strncmp(buff+48,"QZS",3)) *tsys=TSYS_QZS; /* ver.3.02 */
        else if (!strncmp(buff+48,"BDT",3)) *tsys=TSYS_CMP; /* ver.3.02 */
        else if (!strncmp(buff+48,"IRN",3)) *tsys=TSYS_IRN; /* ver.3.03 */
    }  根据历元开始时间第49-51列判断星座类型
    else if (strstr(label,"TIME OF LAST OBS"    )) ; /* opt */
    else if (strstr(label,"RCV CLOCK OFFS APPL" )) ; /* opt */
    else if (strstr(label,"SYS / DCBS APPLIED"  )) ; /* opt ver.3 */
    else if (strstr(label,"SYS / PCVS APPLIED"  )) ; /* opt ver.3 */
    else if (strstr(label,"SYS / SCALE FACTOR"  )) ; /* opt ver.3 */
    else if (strstr(label,"SYS / PHASE SHIFTS"  )) ; /* ver.3.01 */
    else if (strstr(label,"GLONASS SLOT / FRQ #")) { /* ver.3.02 */
        if (nav) {
            for (i=0,p=buff+4;i<8;i++,p+=8) {
                if (sscanf(p,"R%2d %2d",&prn,&fcn)<2) continue;
                if (1<=prn&&prn<=MAXPRNGLO) nav->glo_fcn[prn-1]=fcn+8;
            }
        } 
    }
    else if (strstr(label,"GLONASS COD/PHS/BIS" )) { /* ver.3.02 */
        if (nav) {
            for (i=0,p=buff;i<4;i++,p+=13) {
                if      (strncmp(p+1,"C1C",3)) nav->glo_cpbias[0]=str2num(p,5,8);
                else if (strncmp(p+1,"C1P",3)) nav->glo_cpbias[1]=str2num(p,5,8);
                else if (strncmp(p+1,"C2C",3)) nav->glo_cpbias[2]=str2num(p,5,8);
                else if (strncmp(p+1,"C2P",3)) nav->glo_cpbias[3]=str2num(p,5,8);
            }
        } GLONASS数据处理不做介绍
    }
    else if (strstr(label,"LEAP SECONDS"        )) { /* opt */
        if (nav) nav->leaps=(int)str2num(buff,0,6);
    } 记录跳秒,GLONASS观测文件中独有
    else if (strstr(label,"# OF SALTELLITES"    )) { /* opt */
        /* skip */ ;
    }
    else if (strstr(label,"PRN / # OF OBS"      )) { /* opt */
        /* skip */ ;
    }
} 其余不做记录

这个函数是对观测文件o文件重新编码,以方便后序rtklib直接进行处理,需要对rinex2/3版本的观测文件格式比较了解才能够进行有效的学习,读者可以在阅读源代码的过程中将两个版本的观测文件放在旁边对比学习,笔者便是这么做的。通过buff与label精确定位读取字节进行对比,然后将涉及测站类型的数据例如天线类型、系列号、版本等信息放在sta这个结构体中,而对于不同星座不同的观测数据类型放置在三维数组tobs中。这里最主要的是要明白每个函数的作用,对读取观测文件头部有个脉络,以方便日后在进行算法分析的时候明白原始数据的储存位置,方便使用和查看。

str2num-函数

extern double str2num(const char *s, int i, int n)
{
    double value;
    char str[256],*p=str;
    
    if (i<0||(int)strlen(s)<i||(int)sizeof(str)-1<n) return 0.0;  检验i必须大于0,起始的i位置必须小于原字符串的长度,复制总数必须小于str数组的长度
    for (s+=i;*s&&--n>=0;s++) *p++=*s=='d'||*s=='D'?'E':*s;  将原字符串s全部复制到str数组中,若其中有 d/D 改成 E 进行存储
    *p='\0';   末尾加上终止符
    return sscanf(str,"%lf",&value)==1?value:0.0; 将str以长浮点型格式放入value,成功则返回value值,不成功返回0
}

s是需要复制的字符串,i是从s中第i个字符开始,一直截取n位,然后将截取的字符串转化为数值类型。这个函数是将字符串转化为double类型的数值的函数,利用sscanf函数实现,具体函数的解释都在上面的注释中。对于d/D改成E读者可能有疑惑?举个例子您就明白了,例如“123E01”这个字符串会被转化为1230。但是只有其中的字母是E的时候才象征着科学计数法。星历中使用D表示科学计数法,所以在函数中先将D替换为E,然后进行转化。

decode_navh-函数

static void decode_navh(char *buff, nav_t *nav)
{
    int i,j;
    char *label=buff+60;
    
    trace(4,"decode_navh:\n");
    
    if      (strstr(label,"ION ALPHA"           )) { /* opt ver.2 */
        if (nav) {
            for (i=0,j=2;i<4;i++,j+=12) nav->ion_gps[i]=str2num(buff,j,12);
        }
    }
    else if (strstr(label,"ION BETA"            )) { /* opt ver.2 */
        if (nav) {
            for (i=0,j=2;i<4;i++,j+=12) nav->ion_gps[i+4]=str2num(buff,j,12);
        }
    }
    else if (strstr(label,"DELTA-UTC: A0,A1,T,W")) { /* opt ver.2 */
        if (nav) {
            for (i=0,j=3;i<2;i++,j+=19) nav->utc_gps[i]=str2num(buff,j,19);
            for (;i<4;i++,j+=9) nav->utc_gps[i]=str2num(buff,j,9);
        }
    }
    else if (strstr(label,"IONOSPHERIC CORR"    )) { /* opt ver.3 */
        if (nav) {
            if (!strncmp(buff,"GPSA",4)) {
                for (i=0,j=5;i<4;i++,j+=12) nav->ion_gps[i]=str2num(buff,j,12);
            }
            else if (!strncmp(buff,"GPSB",4)) {
                for (i=0,j=5;i<4;i++,j+=12) nav->ion_gps[i+4]=str2num(buff,j,12);
            }
            else if (!strncmp(buff,"GAL",3)) {
                for (i=0,j=5;i<4;i++,j+=12) nav->ion_gal[i]=str2num(buff,j,12);
            }
            else if (!strncmp(buff,"QZSA",4)) { /* v.3.02 */
                for (i=0,j=5;i<4;i++,j+=12) nav->ion_qzs[i]=str2num(buff,j,12);
            }
            else if (!strncmp(buff,"QZSB",4)) { /* v.3.02 */
                for (i=0,j=5;i<4;i++,j+=12) nav->ion_qzs[i+4]=str2num(buff,j,12);
            }
            else if (!strncmp(buff,"BDSA",4)) { /* v.3.02 */
                for (i=0,j=5;i<4;i++,j+=12) nav->ion_cmp[i]=str2num(buff,j,12);
            }
            else if (!strncmp(buff,"BDSB",4)) { /* v.3.02 */
                for (i=0,j=5;i<4;i++,j+=12) nav->ion_cmp[i+4]=str2num(buff,j,12);
            }
            else if (!strncmp(buff,"IRNA",4)) { /* v.3.03 */
                for (i=0,j=5;i<4;i++,j+=12) nav->ion_irn[i]=str2num(buff,j,12);
            }
            else if (!strncmp(buff,"IRNB",4)) { /* v.3.03 */
                for (i=0,j=5;i<4;i++,j+=12) nav->ion_irn[i+4]=str2num(buff,j,12);
            }
        }
    }
    else if (strstr(label,"TIME SYSTEM CORR"    )) { /* opt ver.3 */
        if (nav) {
            if (!strncmp(buff,"GPUT",4)) {
                nav->utc_gps[0]=str2num(buff, 5,17);
                nav->utc_gps[1]=str2num(buff,22,16);
                nav->utc_gps[2]=str2num(buff,38, 7);
                nav->utc_gps[3]=str2num(buff,45, 5);
            }
            else if (!strncmp(buff,"GLUT",4)) {
                nav->utc_glo[0]=str2num(buff, 5,17);
                nav->utc_glo[1]=str2num(buff,22,16);
            }
            else if (!strncmp(buff,"GAUT",4)) { /* v.3.02 */
                nav->utc_gal[0]=str2num(buff, 5,17);
                nav->utc_gal[1]=str2num(buff,22,16);
                nav->utc_gal[2]=str2num(buff,38, 7);
                nav->utc_gal[3]=str2num(buff,45, 5);
            }
            else if (!strncmp(buff,"QZUT",4)) { /* v.3.02 */
                nav->utc_qzs[0]=str2num(buff, 5,17);
                nav->utc_qzs[1]=str2num(buff,22,16);
                nav->utc_qzs[2]=str2num(buff,38, 7);
                nav->utc_qzs[3]=str2num(buff,45, 5);
            }
            else if (!strncmp(buff,"BDUT",4)) { /* v.3.02 */
                nav->utc_cmp[0]=str2num(buff, 5,17);
                nav->utc_cmp[1]=str2num(buff,22,16);
                nav->utc_cmp[2]=str2num(buff,38, 7);
                nav->utc_cmp[3]=str2num(buff,45, 5);
            }
            else if (!strncmp(buff,"SBUT",4)) { /* v.3.02 */
                nav->utc_cmp[0]=str2num(buff, 5,17);
                nav->utc_cmp[1]=str2num(buff,22,16);
                nav->utc_cmp[2]=str2num(buff,38, 7);
                nav->utc_cmp[3]=str2num(buff,45, 5);
            }
            else if (!strncmp(buff,"IRUT",4)) { /* v.3.03 */
                nav->utc_irn[0]=str2num(buff, 5,17);
                nav->utc_irn[1]=str2num(buff,22,16);
                nav->utc_irn[2]=str2num(buff,38, 7);
                nav->utc_irn[3]=str2num(buff,45, 5);
            }
        }
    }
    else if (strstr(label,"LEAP SECONDS"        )) { /* opt */
        if (nav) nav->leaps=(int)str2num(buff,0,6);
    }
}

此函数主要是应用字符串比较strstr函数和strncmp函数实现对头文件描述部分进行配对,然后使用str2num函数实现文本文件到数字的转换,从而读取数据。

readrnxh-函数

while (fgets(buff,MAXRNXLEN,fp)) {
        
        if (strlen(buff)<=60) continue;
        
        else if (strstr(label,"RINEX VERSION / TYPE")) {
            *ver=str2num(buff,0,9);
            *type=*(buff+20);
            
            /* satellite system */
            switch (*(buff+40)) {
                case ' ':
                case 'G': *sys=SYS_GPS;  *tsys=TSYS_GPS; break;
                case 'R': *sys=SYS_GLO;  *tsys=TSYS_UTC; break;
                case 'E': *sys=SYS_GAL;  *tsys=TSYS_GAL; break; /* v.2.12 */
                case 'S': *sys=SYS_SBS;  *tsys=TSYS_GPS; break;
                case 'J': *sys=SYS_QZS;  *tsys=TSYS_QZS; break; /* v.3.02 */
                case 'C': *sys=SYS_CMP;  *tsys=TSYS_CMP; break; /* v.2.12 */
                case 'I': *sys=SYS_IRN;  *tsys=TSYS_IRN; break; /* v.3.03 */
                case 'M': *sys=SYS_NONE; *tsys=TSYS_GPS; break; /* mixed */
                default :
                    trace(2,"not supported satellite system: %c\n",*(buff+40));
                    break;
            }
            continue;
        }
        /* file type */
        switch (*type) {
            case 'O': decode_obsh(fp,buff,*ver,tsys,tobs,nav,sta); break;
            case 'N': decode_navh (buff,nav); break;
            case 'G': decode_gnavh(buff,nav); break;
            case 'H': decode_hnavh(buff,nav); break;
            case 'J': decode_navh (buff,nav); break; /* extension */
            case 'L': decode_navh (buff,nav); break; /* extension */
        }

此函数主要是对真正输入文件进行读取,通过第一行的41识别符进行文件类型的判断,并用switch语句对刚刚获取的type进行读取,然后调用之前写的重新编码的函数,实现具体功能,例如*type=O,则调用刚刚的decode_obsh函数,进行头文件相关重要信息的存储。

参考

学习rtklib(二)
头文件的处理到这里基本形成体系,接下来的系列会介绍具体文件的实际数据的读取与存储。

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: RINEX(Receiver INdependent EXchange)格式是一种用于GPS的数据交换格式,它包含了卫星观测数据、钟差、导航电文等信息。在此,我将提供一个基于Python语言的Rinex O文件Rinex N文件读取类的设计。 首先,我们需要定义一个RinexFile类,用于处理Rinex文件读取解析。这个类包含了以下方法: 1. `__init__(self, filepath: str)`: 初始化方法,传入Rinex文件的路径,并打开文件。 2. `read_header(self)`: 读取Rinex文件头信息,将其保存在一个字典中。 3. `read_obs(self)`: 读取Rinex O文件中的观测数据,并将其保存在一个列表中。 4. `read_nav(self)`: 读取Rinex N文件中的导航电文数据,并将其保存在一个列表中。 5. `show_header(self)`: 显示Rinex文件头信息。 6. `show_obs(self)`: 显示Rinex O文件中的观测数据。 7. `show_nav(self)`: 显示Rinex N文件中的导航电文数据。 下面是代码实现: ```python class RinexFile: def __init__(self, filepath: str): self.filepath = filepath self.file = open(self.filepath, 'r') self.header = {} def read_header(self): line = self.file.readline() while not line.startswith('END OF HEADER'): label = line[60:].strip() if label == '': label = line[20] if label not in self.header: self.header[label] = [] self.header[label].append(line[:60].strip()) line = self.file.readline() def read_obs(self): obs = [] line = self.file.readline() while line: if line.startswith('>'): rec = {'time': line[1:27].strip()} line = self.file.readline() while line and not line.startswith('>'): sat, obs_data = line[:3], line[3:].split() rec[sat] = [float(x) if x != ' ' else None for x in obs_data] line = self.file.readline() obs.append(rec) else: line = self.file.readline() return obs def read_nav(self): nav = [] line = self.file.readline() while line: if line.startswith('G'): rec = {'sat': line[:3], 'toc': line[4:23].strip()} line = self.file.readline() while line and not line.startswith('G'): rec[line[0]] = line[3:].strip() line = self.file.readline() nav.append(rec) else: line = self.file.readline() return nav def show_header(self): for label, lines in self.header.items(): print(label) for line in lines: print(line) def show_obs(self): obs = self.read_obs() for rec in obs: print(rec['time']) for sat in rec: if sat != 'time': print(sat, rec[sat]) def show_nav(self): nav = self.read_nav() for rec in nav: print(rec['sat'], rec['toc']) for field, value in rec.items(): if field not in ['sat', 'toc']: print(field, value) ``` 接下来,我们可以使用这个类来读取和显示Rinex文件中的数据。例如,我们可以读取一个Rinex O文件,并显示其中的观测数据: ```python rinex_file = RinexFile('example.obs') rinex_file.show_header() rinex_file.show_obs() ``` 同样地,我们也可以读取一个Rinex N文件,并显示其中的导航电文数据: ```python rinex_file = RinexFile('example.nav') rinex_file.show_header() rinex_file.show_nav() ``` 以上就是基于Python语言的Rinex O文件Rinex N文件读取类的设计和实现,希望能对您有所帮助。 ### 回答2: 设计 RinexO 文件RinexN 文件读取类,可以将 Rinex 文件的内容读取并显示。 Rinex(使用RINEX 格式存储的 GPS 接收机观测数据)是一种常见的数据格式,适用于 GPS 数据的存储和交换。要实现对 Rinex 文件内容的读取和显示,可以使用以下步骤来设计相关的类。 首先,设计一个 Rinex 文件读取类(RinexReader),其中包含读取 Rinex 文件的方法(readFile),该方法接受 Rinex 文件路径作为输入参数。 在 RinexReader 中,可以使用文件操作来读取 Rinex 文件的内容。可以逐行读取文件,并使用适当的数据结构来存储每一行的内容。可以将读取到的 Rinex 内容存储在一个列表或数组中,方便后续的显示。 接下来,设计一个 Rinex 内容显示类(RinexDisplay),其中包含展示 Rinex 文件内容的方法(displayContent),该方法接受 Rinex 内容列表或数组作为输入参数。 在 RinexDisplay 中,可以遍历 Rinex 内容列表或数组,并逐行显示 Rinex 内容。可以使用控制台输出或者图形界面来展示 Rinex 文件的内容。 最后,在主程序中,实例化 RinexReader 和 RinexDisplay 类,并相应调用 readFile 和 displayContent 方法。首先使用 RinexReader 读取 Rinex 文件内容,并将读取到的内容传递给 RinexDisplay 来显示。 这样,就实现了对 Rinex 文件内容的读取和显示。 ### 回答3: Rinex O文件Rinex N文件都是GPS观测数据格式的一种,用于记录卫星定位系统的信号观测值。设计Rinex O文件Rinex N文件读取类,可以实现对Rinex文件内容的读取和显示。 首先,需要创建一个RinexFile类,用于表示Rinex文件。该类需要包含读取和显示Rinex文件内容的方法。 其次,需要创建一个RinexOParser类和一个RinexNParser类,分别用于解析Rinex O文件Rinex N文件的内容。这两个解析器类都需要继承RinexFile类,并实现读取和显示文件内容的方法。 在RinexOParser类和RinexNParser类中,可以通过读取Rinex文件的每一行,并根据不同的数据类型(如观测历元、卫星编号、观测值等)对数据进行解析和处理。解析后的数据可以存储在类的成员变量中,以备后续显示使用。 在RinexFile类中,可以实现显示文件内容的方法,以将解析后的数据以友好的方式展示给用户。例如,可以按观测历元的顺序逐行打印出每个历元的观测值,包括卫星编号以及观测值的各个分量(例如伪距、载波相位等)。 通过使用RinexOParser类和RinexNParser类,可以读取和显示Rinex文件的内容。用户只需创建对应的解析器对象,并调用解析器对象的读取和显示方法即可。 总之,设计Rinex O文件Rinex N文件读取类,可以通过解析Rinex文件的内容,将观测数据展示给用户。这样用户可以方便地查看和分析Rinex文件中的GPS观测值。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值