目录
2.处理配置文件函数:proccfgfile(cfgfile)
3.batchProc()函数:处理一个文件夹的下的多个.o观测值文件
4.procOneFile()函数:逐个处理单个.o观测值文件
5.preProc()函数:获取该观测值文件的开始和结束时间
1.进入main()函数
在“项目属性页->配置属性->调试->命令参数”下设置好".cfg"文件的位置,也就是gamp软件的配置参数文件,如下图所示,然后直接运行VS软件的调试命令,进入主函数main():
这里解释一下main()函数传入的两个参数,一个是int类型的argc,另一个是char**类型的argv,argc代表的是参数的个数,argv代表的是实际的参数,以该程序为例,argv[0]表示的是:0x034dba84 "D:\\BDS_Version_GAMP\\BDS_Version_GAMP\\Debug\\BDS_Version_GAMP.exe",是.exe类型的可执行程序,argv[1]表示的是:0x034dbac4 "D:\\1A_GNSS_Data\\20240101\\gamp.cfg",也就是我在第一步写的.cfg文件的配置文件路径,
2.处理配置文件函数:proccfgfile(cfgfile)
1.创建一个指针变量cfgfile用于存储argv[1](即配置文件的路径);
2.t1和t2用于记录程序运行的开始和结束时间
3.进入proccfgfile(cfgfile)函数,传入指针cfgfile,处理配置文件选项
/* GAMP main -------------------------------------------------------------*/
int main(int argc, char **argv)
{
//char cfgfile[1000]="C:\\mannual_GAMP\\Examples\\2017244\\gamp.cfg";
char *cfgfile;
long t1,t2;
t1=clock();
if (argc==1) {
printf("\n * The input command-line parameter indicating configure file is lost, please check it!\n");
return 0;
}
else {
cfgfile=argv[1];
}
/* find processing configure file */
proccfgfile(cfgfile);
t2=clock();
printf("\n * The total time for running the program: %6.3f seconds\n%c",(double)(t2-t1)/CLOCKS_PER_SEC,'\0');
//printf("Press any key to exit!\n");
//getchar();
return 0;
}
进入proccfgfile()函数内部,注释卸载了下面的代码行:
1.该函数主要是判别是处理单个obs文件还是多个obs文件,然后存储输入的文件路径到inputPath下面
2.该函数中,还涉及trimspace(PPP_Glo.inputPath)和cutFilePathsep(PPp Glo.inputPath),这两个函数都是对输入文件的路径进行格式处理的,以适应整个程序的格式;
3.根据PPP_Glo.prcType是0还是1来确定是处理单个.o观测值文件还是整个文件夹folder下的多个.o文件,分别进入proconeFile()和batchProc()函数进行各自的处理。(我默认是进入的batchProc函数)
static int proccfgfile(char cfgfile[])
{
FILE *fp=NULL;
char *p,tmp[MAXSTRPATH]={'\0'};
//initialization
PPP_Glo.prcType=-1;
PPP_Glo.outFolder[0]='\0';
PPP_Glo.inputPath[0]='\0';
if ((fp=fopen(cfgfile,"r"))==NULL) { //把.cfg配置文件赋值为文件流指针fp
printf("*** ERROR: open configure file failed, please check it!\n");
return 0;
}
while (!feof(fp)) { //feof()函数用来 检查文件流 fp 是否没有到达文件(.cfg配置文件)末尾EOF
tmp[0]='\0';
fgets(tmp,MAXSTRPATH,fp); //fget()函数用于从文件流fp中读取一行文本,然后赋值给tmp,MAXSTRPATH表示该行文本的最大字符数量
if ((tmp!=NULL)&&(tmp[0]=='#')) continue; //跳过用"#"表示的注释行
if (strstr(tmp,"obs file/folder")) { //检查配置文件中的一行是否包含特定的关键字("obs file/folder"),如果包含,则进入if语句中进行下一步的操作,否则,...
p=strrchr(tmp,'='); //使用 strrchr() 函数在 tmp 中查找等号 '=' 的最后一次出现,并将其位置(指针)存储在变量 p 中
sscanf(p+1,"%d",&PPP_Glo.prcType); //使用 sscanf() 函数从 p+1 指向的位置(即等号后面的字符串)读取一个整数,并将其存储在 PPP_Glo.prcType 中
tmp[0]='\0'; //清空当前行,用于下一次读取传入tmp值
if (fgets(tmp,MAXSTRPATH,fp)) { //继续读取文件流fp的下一行文本,存储到tmp数组中
p=strrchr(tmp,'=');
sscanf(p+1,"%[^,]",PPP_Glo.inputPath); //读取观测值文件O文件的文件路径位置,将读取的字符串存储在 PPP_Glo.inputPath 中
trimSpace(PPP_Glo.inputPath); //查找字符串中第一个和最后一个非空白字符的位置,然后截取这两个位置之间的子字符串,实现了去除字符串两端空白字符的目的
cutFilePathSep(PPP_Glo.inputPath); //通过循环检查字符串末尾的字符,并且如果发现是路径分隔符,则将其移除。
}
else {
printf("*** ERROR: read obs files path error!");
return 0;
}
break;
}
}
fclose(fp);
if (PPP_Glo.prcType<0||PPP_Glo.prcType>2) {
printf("*** ERROR: read obs files path error!");
return 0;
}
if (PPP_Glo.prcType==0)
procOneFile(PPP_Glo.inputPath,cfgfile,0,1); //处理单个obs观测值文件
else if (PPP_Glo.prcType==1)
batchProc(PPP_Glo.inputPath,cfgfile); //处理文件夹下的多个obs观测值文件
return 1;
}
3.batchProc()函数:处理一个文件夹的下的多个.o观测值文件
1.传入参数为:batchProc(char folder[], char cfgfile[]),分别是处理的输入文件夹,以及配置文件.cfg
2.在当前文件夹下查找所有的.o观测值文件,然后构建一个完整的文件路径,传入procOneFile()函数中,逐个处理单个.o观测值文件,使用i来记录当前处理的观测值文件的个数。
// batch processing ofiles
extern void batchProc(char folder[], char cfgfile[])
{
DIR *dir; //声明了一个指向 DIR 结构的指针 dir,用于存储打开的目录流。
struct dirent *file; //声明了一个指向 dirent 结构的指针 file,用于存储从目录流中读取的每个文件的信息
char filepath[MAXSTRPATH]={'\0'},*ext;
char sep=(char)FILEPATHSEP; //FILEPATHSEP表示文件分隔符,这里已经定义好了,是"\\"
int i=1;
if (!(dir=opendir(folder))) { //尝试打开 folder 指定的目录。如果打开失败,打印错误消息并返回
printf("*** ERROR: open obsdir failed, please check it!\n");
return;
}
while((file=readdir(dir))!=NULL) { //使用 readdir() 函数在打开的目录流中循环读取每个文件的信息,直到返回 NULL
if (strncmp(file->d_name,".",1)==0) continue; //skip ".",".." for linux
if (!(ext=strrchr(file->d_name,'.'))) continue; //使用 strrchr() 函数查找文件名中最后一个点 . 的位置。如果找不到,跳过当前循环迭代
if (!strstr(ext+3,"o")) continue; //从ext位置开始的第三个字符ext+3,如果不是"o",那么就跳过当前循环,这是用来寻找.o观测值文件的,
sprintf(filepath,"%s%c%s",folder,sep,file->d_name);//使用 sprintf() 函数构建完整的文件路径
procOneFile(filepath,cfgfile,0,i++); //调用 procOneFile 函数处理每个文件,传递文件路径、配置文件路径和其他参数,i表示当前处理的文件个数,正在处理第i个文件
}
closedir(dir);
}
4.procOneFile()函数:逐个处理单个.o观测值文件
1.使用t1,t2记录单个.o观测值文件的处理时间
2.procparam_t结构体包括:处理选项结构体prcopt_t、定位解选项结构体solopt_t、文件类型结构体filopt_t
3.包括预处理、读取配置、检查参数、自动匹配文件选项、进行定位处理以及后处理。它还计算并打印程序的运行时间。需要注意的是,代码中有一些未定义的函数和全局变量,如
preProc
、readcfgFile
、gampPos
、postProc
以及PPP_Glo
,这些应该是在其他地方定义的。此外,OUTWIN
、OUTFIL
和OFILE_DEBUG
也是在其他地方定义的常量或宏。(来自kimi.ai)
//processing single ofile
extern void procOneFile(char file[], char cfgfile[], int iT, int iN)
{
procparam_t pparam;
gtime_t t={0},ts=t,te=t ;
long t1,t2;
t1=clock();
preProc(file,&pparam,&ts,&te); //预处理,获取开始时间ts和结束时间te
printf(" * Processing the %dth", iN);
if (iT>0) printf("/%d", iT); //打印输出信息:正在处理第几个观测值文件
printf(" ofile: %s\n", PPP_Glo.ofileName_ful);
//read configure file 读取配置文件,存储到相应的结构体变量中
readcfgFile(cfgfile,&pparam.prcopt,&pparam.solopt,&pparam.filopt);
//single-, dual- or triple-frequency?
if (pparam.prcopt.ionoopt==IONOOPT_IF12||pparam.prcopt.ionoopt==IONOOPT_UC1) { //如果选择IF组合和UC单频,那么就需要在配置文件中设置nf,也就是频率是1,否则会报错
if (pparam.prcopt.nf!=1) {
printf("*** ERROR: Number of frequencies Error! Please set inpfrq=1.\n");
return;
}
}
if (pparam.prcopt.ionoopt==IONOOPT_UC12) { // UC12组合,双频非差非组合
if (pparam.prcopt.nf!=2) {
printf("*** ERROR: Number of frequencies Error! Please set inpfrq=2.\n");
return;
}
}
//processing time set
if (!PPP_Glo.prcOpt_Ex.bTsSet) PPP_Glo.prcOpt_Ex.ts=ts;
else if (timediff(ts,PPP_Glo.prcOpt_Ex.ts)>0) PPP_Glo.prcOpt_Ex.ts=ts;
if (!PPP_Glo.prcOpt_Ex.bTeSet) PPP_Glo.prcOpt_Ex.te=te;
else if (timediff(te,PPP_Glo.prcOpt_Ex.te)<0) PPP_Glo.prcOpt_Ex.te=te;
/* 区分北斗二号系统还是三号系统或者同时选择两个系统 */
if (pparam.prcopt.BDSselect == 1)
{
for (int z = 115; z <= 160; z++)
pparam.prcopt.exsats[z] = 1;
}
if (pparam.prcopt.BDSselect == 2)
{
for (int z = 97; z <= 114; z++)
pparam.prcopt.exsats[z] = 1;
}
//automatically matches the corresponding files 自动匹配相应的文件
getFopt_auto(file,PPP_Glo.obsDir,ts,te,pparam.prcopt,pparam.solopt,&pparam.filopt);
// post processing positioning 后处理定位,最关键,进入到处理定位的部分
gampPos(PPP_Glo.prcOpt_Ex.ts, PPP_Glo.prcOpt_Ex.te, 0.0, 0.0,
&pparam.prcopt,&pparam.solopt,&pparam.filopt);
postProc(pparam);
t2=clock();
//打印处理该观测值文件所需要的时间
sprintf(PPP_Glo.chMsg," * The program runs for %6.3f seconds\n%c",(double)(t2-t1)/CLOCKS_PER_SEC,'\0');
//输出debug信息到控制台
outDebug(OUTWIN,OUTFIL,0);
printf("/***************************** OK *****************************/\n\n\n");
//输出文件流
if (PPP_Glo.outFp[OFILE_DEBUG]) {
fclose(PPP_Glo.outFp[OFILE_DEBUG]);
PPP_Glo.outFp[OFILE_DEBUG]=NULL;
}
}
5.preProc()函数:获取该观测值文件的开始和结束时间
preProc()
//get informations of start and end time etc.
static int preProc(char *file, procparam_t *pparam, gtime_t *ts, gtime_t *te)
{
int i;
double delta[3];
char anttype[100],rcvtype[40];
pparam->filopt=filopt_default;
pparam->solopt=solopt_default;
pparam->prcopt=prcopt_default;
for (i=0;i<MAXINFILE;i++) pparam->filopt.inf[i]=NULL;
for (i=0;i<MAXOUTFILE;i++) pparam->filopt.outf[i]=NULL;
initGlobal(&PPP_Glo); //初始化PPP定位的全局变量PPPGlobal_t PPP_Glo,很重要的一个全局变量!!!PPP_Glo在结构体定义的时候就创建了该对象,也是在整个程序中都会用得到
getObsInfo(file,anttype,rcvtype,delta,ts,te,PPP_Glo.sitName,
PPP_Glo.ofileName,PPP_Glo.ofileName_ful,PPP_Glo.obsExt);
//存储到相应的结构体变量中
xStrMid(PPP_Glo.prcOpt_Ex.rcvType,0,20,rcvtype);
xStrMid(pparam->prcopt.anttype,0,20,anttype);
//antdel: E,N,U
pparam->prcopt.antdel[0]=delta[1];
pparam->prcopt.antdel[1]=delta[2];
pparam->prcopt.antdel[2]=delta[0];
pparam->prcopt.bdsisb=0;//LZ-0330 !!!!!!!!!! bds23isb option ( 0:off 1:on )
pparam->prcopt.helmert=0;//LZ-0405 !!!!!!!!!! helmert VCE option ( 0:off 1:spp 2:ppp )
//allocation for 'inf' and 'outf' of 'filopt'
for (i=0;i<MAXINFILE;i++) { **用于为 inf 数组分配内存。MAXINFILE 是一个定义了最大输入文件数量的常量
if (!(pparam->filopt.inf[i]=(char *)malloc(MAXSTRPATH))) { //在每次迭代中,使用 malloc 函数为 inf 数组的每个元素分配 MAXSTRPATH 字节的内存。MAXSTRPATH 是一个定义了最大字符串长度的常量。如果 malloc 返回 NULL,表示内存分配失败;MAXSTRPATH 的大小只是决定了你能够处理的文件路径字符串的最大长度,而不是文件的实际大小,而是指字符串缓冲区的大小,用于存储文件的路径或名称
for (i--;i>=0;i--) free(pparam->filopt.inf[i]);
return 0; //如果内存分配失败,函数返回 0,表示失败。
}
else //如果内存分配成功,将新分配的内存的第一个字符设置为 '\0',即空字符,这将初始化字符串
pparam->filopt.inf[i][0]='\0';
}
for (i=0;i<MAXOUTFILE;i++) { //这个循环用于为 outf 数组分配内存。MAXOUTFILE 是一个定义了最大输出文件数量的常量。
if (!(pparam->filopt.outf[i]=(char *)malloc(MAXSTRPATH))) {
for (i--;i>=0;i--) free(pparam->filopt.outf[i]);
return 0;
}
else
pparam->filopt.outf[i][0]='\0';
}
return 1;
}
initGlobal(PPPGlobal_t *ppp):初始化PPPGlobal_t全局变量,很重要的一个结构体
//init PPP_Global struct
static void initGlobal(PPPGlobal_t *ppp)
{
int i;
gtime_t gt0={0};
ssatEx_t ssat_EX0={{0}};
ppp->prcOpt_Ex.navSys=SYS_GPS;
ppp->prcOpt_Ex.solType=0;
ppp->prcOpt_Ex.posMode=PMODE_PPP_STATIC;
ppp->prcOpt_Ex.rcvType[0]='\0';
ppp->sitName[0]='\0';
for (i=0;i<4;i++) ppp->crdTrue[i]=0.0;
for (i=0;i<3;i++) ppp->rr[i]=0.0;
ppp->chMsg[0]='\0';
ppp->chTime[0]='\0';
ppp->ofileName_ful[0]='\0';
ppp->ofileName[0]='\0';
ppp->obsDir[0]='\0';
ppp->obsExt[0]='\0';
ppp->nEpoch=0;
ppp->iEpoch=0;
ppp->iObsu=0;
ppp->revs=0;
if (ppp->solf) free(ppp->solf);
ppp->solf=NULL;
if (ppp->solb) free(ppp->solb);
ppp->solb=NULL;
ppp->iSolf=0;
ppp->iSolb=0;
ppp->dintv=0.0;
ppp->delEp=0;
ppp->sample=0.0;
ppp->zhd=0.0;
ppp->clkJump=0;
for (i=0;i<MAXSAT;i++) {
ppp->ssat_Ex[i]=ssat_EX0;
}
ppp->prcOpt_Ex.bElevCheckEx=0;
ppp->prcOpt_Ex.tropMF=TROPMF_GMF;
ppp->prcOpt_Ex.ionopnoise=1; //0: static 1: random walk 2:white noise
ppp->prcOpt_Ex.ion_const=0; //0: off 1: on
//cycle slip set
ppp->prcOpt_Ex.csThresGF=0.0;
ppp->prcOpt_Ex.csThresMW=0.0;
ppp->prcOpt_Ex.bUsed_gfCs=0;
ppp->prcOpt_Ex.bUsed_mwCs=0;
ppp->prcOpt_Ex.tPrcUnit=0.0;
//measurement error ratio
ppp->prcOpt_Ex.errRatioGLO=100.0;
ppp->prcOpt_Ex.errRatioBDS=100.0;
ppp->prcOpt_Ex.errRatioGAL=100.0;
ppp->prcOpt_Ex.errRatioQZS=100.0;
//process-noise std for random walk new
ppp->prcOpt_Ex.prn_iono=0.001; //m/sqrt(s)
//time set
ppp->prcOpt_Ex.bTsSet=0;
ppp->prcOpt_Ex.bTeSet=0;
ppp->tNow=gt0;
ppp->t_30min=gt0;
ppp->isb_30min=0.0;
ppp->sowNow=0.0;
//for SPP
ppp->nBadEpSPP=0;
ppp->bOKSPP=1;
//对每个卫星根据各个系统中的卫星数目大小设置一个初始卫星系统G/C/R/E,sys=1,4,8,16,32 以及卫星的id,id:eg.C63、C01、G02、R01、E01
for (i=0;i<MAXSAT;i++) {
ppp->sFlag[i].sys=satsys(i+1,&ppp->sFlag[i].prn);
satno2id(i+1,ppp->sFlag[i].id);
}
for (i=0;i<=MAXPRNGLO;i++) ppp->fnGlo[i]=0;
}
6.getObsInfo()函数:获取观测值信息
getObsInfo(file,anttype,rcvtype,delta,ts,te,PPP_Glo.sitName,
PPP_Glo.ofileName,PPP_Glo.ofileName_ful,PPP_Glo.obsExt):
//read the information of time tag of the first and last epoch, et al.
extern int getObsInfo(char ofilepath[], char anttype[], char rcvtype[],
double delta[3], gtime_t* ts, gtime_t* te, char *sitename,
char *filename, char *filename_ful, char *ext)
{
FILE *fp=NULL;
char *p,*q;
char oneline[300]={'\0'};
int ilen=strlen(ofilepath); //计算观测文件路径字符串 ofilepath 的长度,ofilepath:eg.D:\1A_GNSS_Data\20240101\JOZ20010.24o
double cts[6],cte[6],rnxver=2.11;
if ((p=strrchr(ofilepath,FILEPATHSEP))) //使用 strrchr() 函数查找路径字符串中最后一个路径分隔符,并根据它来提取观测数据目录
xStrMid(PPP_Glo.obsDir,0,p-ofilepath,ofilepath); //obsDir:eg.D:\1A_GNSS_Data\20240101,xStrMid()函数的作用是提取文件的上一级目录,eg:D:\1A_GNSS_Data\20240101\JOZ20010.24o的上一级目录为D:\1A_GNSS_Data\20240101,将其赋值给第一个变量值:obsDir,也是全局变量结构体中的变量值
if ((p=strrchr(ofilepath,FILEPATHSEP))&&(q=strrchr(ofilepath,'.'))) { //使用 strrchr 函数分别查找 ofilepath 中最后一次出现的路径分隔符和点(.)。如果两者都存在,则进入条件语句块。
if (ext) {xStrMid(ext,q-ofilepath+1,ilen-(q-ofilepath+1),ofilepath);} //if(ext)表示如果ext不是空指针,就可以进行语句块的操作,ext是作为参数传入的,也就是全局变量结构体PPP_Glo中的变量,在此之前已经被初始化了,因此不会为空;ext表示观测值文件的后三位:例如:24o
if (sitename) {strncpy(sitename,p+1,4); sitename[4]='\0';} //如果提供了 sitename 参数(用于存储站点名称),则使用 strncpy 函数从路径分隔符之后的位置开始复制4个字符到 sitename。然后确保 sitename 是以空字符终止的字符串。
if (filename) {strncpy(filename,p+1,q-p-1); filename[q-p-1]='\0';} //filename:eg.JOZ20010
if (filename_ful) {strncpy(filename_ful,p+1,ilen-(p-ofilepath));filename_ful[ilen-(p-ofilepath)]='\0';} //filename_ful:eg.JOZ20010.24o
}
fp=fopen(ofilepath,"r"); //以只读的方式打开ofilepath观测值.o文件
if (fp==NULL) {
printf("*** ERROR: read file %s error!\n",ofilepath);
return 0;
}
//get antenna type et al. from header of observation file,从观测值文件的头部获取天线类型
while (fgets(oneline,MAXCHARS,fp)) { //使用 fgets 函数从文件指针 fp 指向的文件中读取一行文本,直到文件末尾。oneline 是一个字符数组,用于存储读取的每一行文本,MAXCHARS 是定义的最大字符数
if(strstr(oneline,"ANT # / TYPE")&&anttype!=NULL) //检查当前行是否包含 "ANT # / TYPE" 字符串,这通常表示天线类型信息。如果找到了,并且 anttype 指针不是 NULL
xStrMid(anttype,20,20,oneline); //该函数是取该行文本中间的字符串,左中右三个字符串,调用xStrMid()函数取中间这个字符串传入anttype变量中存储
if(strstr(oneline,"REC # / TYPE / VERS")&&rcvtype!=NULL)//获取接收机类型
xStrMid(rcvtype,20,20,oneline);
if(strstr(oneline,"ANTENNA: DELTA H/E/N")&&delta!=NULL) { //获取天线的ENU delta桌值
delta[0]=str2num(oneline,0,14);
delta[1]=str2num(oneline,14,14);
delta[2]=str2num(oneline,28,14);
}
if(strstr(oneline,"RINEX VERSION / TYPE") ) { //rinex格式的版本:eg.3.04
rnxver=str2num(oneline,0,9);
}
if(strstr(oneline,"END OF HEADER")) //读取到END OF HEADER,则头文件读取结束
break;
}
getFirstTime(rnxver,fp,cts); //读取观测值文件的开始时间,存入cts变量中
getLastTime(rnxver,fp,cte); //读取观测值文件的终止时间,存储到cte变量中
if (norm(cts,6)<=1.0||norm(cte,6)<=1.0) { //在外部判断cte是否有错误
printf("*** ERROR: norm(cts,6)<=1.0||norm(cte,6)<=1.0");
return 0;
}
if (ts) *ts=epoch2time(cts); //将公历时间cts转换为utc时间ts,utc时间是从1970年1月1日以来的秒数,即Unix时间戳
if (te) *te=epoch2time(cte); //同上
fclose(fp);
return 1;
}
getFirstTime(rnxver,fp,cts):获取观测值文件的第一个历元时间
//read time tag of the first epoch
static void getFirstTime(double rnxver, FILE *fp, double cts[6])
{
char oneline[MAXCHARS];
int i,ilen;
for (i=0;i<6;i++) cts[i]=0.0;
while (!feof(fp)) {
if (rnxver<=2.99) {
//read time tag of the first epoch
fgets(oneline,sizeof(oneline),fp);
i=myRound(str2num(oneline,28,1));
if (i==2) continue;
if (i==4) {
ilen=myRound(str2num(oneline,30,2));
for (i=0;i<ilen;i++)
fgets(oneline,sizeof(oneline),fp);
continue;
}
if (strstr(oneline,"COMMENT")||strstr(oneline,"comment")) continue;
if (oneline[3]!=' '||oneline[6]!=' '||oneline[9]!=' '||
oneline[12]!=' '||oneline[15]!=' ')
continue;
if ((oneline[1]==' '&&oneline[2]==' ')||
(oneline[4]==' '&&oneline[5]==' '))
continue;
cts[0]=str2num(oneline,1,2);
if(cts[0]<=79.0&&cts[0]>=0.0) cts[0]+=2000.0;
else cts[0]+=1900.0;
cts[1]=str2num(oneline,4,2);
cts[2]=str2num(oneline,7,2);
cts[3]=str2num(oneline,10,2);
cts[4]=str2num(oneline,13,2);
cts[5]=str2num(oneline,15,11);
}
else { //以3.04版本的rinex格式为例,执行else下面的语句块,
//read time tag of the first epoch
fgets(oneline,sizeof(oneline),fp); //从文件中读取一行文本;在 fgets 函数中使用 sizeof(oneline) 作为参数是为了告诉函数 oneline 数组可以存储的最大字符数,这样 fgets 就不会读取超过数组大小的字符,从而避免缓冲区溢出的风险。
if (oneline[0]!='>') continue;
if (strstr(oneline,"COMMENT")||strstr(oneline,"comment"))
continue;
if (oneline[1]!=' '||oneline[6]!=' '||oneline[9]!=' '||
oneline[12]!=' '||oneline[15]!=' ')
continue;
cts[0]=str2num(oneline,2,4); //从第2个位置开始,读取4个字符,存入到cts[0]中,str2num()函数是用来将字符串转换为数字的;在此处,cts[0]表示年,eg.2024
cts[1]=str2num(oneline,7,2); //月
cts[2]=str2num(oneline,10,2); //日
cts[3]=str2num(oneline,13,2); //时
cts[4]=str2num(oneline,16,2); //分
cts[5]=str2num(oneline,19,10); //秒
if (norm(cts,6)<=1.0) continue; //以防时间错误
}
break;
}
}
getLastTime(rnxver,fp,cte):获取观测值文件的最后一个历元时间
//read time tag of the last epoch
static void getLastTime(double rnxver, FILE *fp, double cte[6])
{
int i,n,len;
char oneline[MAXCHARS];
for (i=0;i<6;i++) cte[i]=0.0;
i=fseek(fp,-20000L,SEEK_END); //尝试将文件指针从文件末尾向前移动 20000 个字节(注意这里是字节,不是行数,应该是根据可能的最大字节数来写入的),以便于从文件末尾开始搜索
if (i!=0) { //如果 fseek 失败(返回非零值),则尝试其他方法定位到文件的末尾
fseek(fp,0L,SEEK_END); //将文件指针移动到文件末尾。
len=ftell(fp); //获取当前文件指针的位置,即文件的总长度。
if (len>8000) //如果文件长度大于 8000 字节,再次尝试向前移动 8000 个字节。
i=fseek(fp,-8000L,SEEK_END);
else
i=fseek(fp,0L,SEEK_SET);
}
if (i!=0) { //如果再次 fseek 失败,则将 cte[0] 设置为 2100.0 并返回,表示无法找到时间标签。
cte[0]=2100.0;
return;
}
while (!feof(fp)) { //使用 feof 函数检查文件指针是否到达文件末尾,如果没有,则进入循环。
oneline[0]='\0'; //如果没有,则进入循环。
fgets(oneline,sizeof(oneline),fp);
if (rnxver<=2.99) {
if (strlen(oneline)<26) continue;
if (strstr(oneline,"COMMENT")||strstr(oneline,"comment"))
continue;
if (oneline[3]!=' '||oneline[6]!=' '||oneline[9]!=' '||
oneline[12]!=' '||oneline[15]!=' ')
continue;
if ((oneline[1]==' '&&oneline[2]==' ')||(oneline[4]==' '&&oneline[5]==' '))
continue;
cte[0]=str2num(oneline,1,2);
if(cte[0]<=79.0&&cte[0]>=0.0) cte[0]+=2000.0;
else cte[0]+=1900.0;
cte[1]=str2num(oneline,4,2);
cte[2]=str2num(oneline,7,2);
cte[3]=str2num(oneline,10,2);
cte[4]=str2num(oneline,13,2);
cte[5]=str2num(oneline,15,11);
}
else {
if (strlen(oneline)<29) continue;
if (oneline[0]!='>') continue;
if (strstr(oneline,"COMMENT")||strstr(oneline,"comment"))
continue;
if (oneline[1]!=' '||oneline[6]!=' '||oneline[9]!=' '||
oneline[12]!=' '||oneline[15]!=' ')
continue;
if (myRound(str2num(oneline,30,2))==4) {
n=myRound(str2num(oneline,32,3));
for (i=0;i<n;i++) {
oneline[0]='\0';
fgets(oneline,sizeof(oneline),fp);
}
continue;
}
cte[0]=str2num(oneline,2,4); //年
cte[1]=str2num(oneline,7,2); //月
cte[2]=str2num(oneline,10,2); //日
cte[3]=str2num(oneline,13,2); //时
cte[4]=str2num(oneline,16,2); //分
cte[5]=str2num(oneline,19,10); //秒
//注意:找最后一个观测值历元时间的话不能提前返回,因为可能找的是倒数第二个或者倒数第三个之类的历元,需要一直查找,直到文件结束;
}
}
6.xStrMid()函数:
xStrMid (char *szDest, const int nPos, const int nCount, char *szSrc):
szDest
是目标字符串的指针,nPos
是开始复制的起始位置,nCount
是要复制的字符数,szSrc
是源字符串的指针。
//string clipping
extern void xStrMid (char *szDest, const int nPos, const int nCount, char *szSrc)
{
int i,n;
char *str,c;
n=strlen(szSrc); //使用 strlen 函数计算源字符串 szSrc 的长度。
if (n<=0) return;
str=szSrc+nPos; //计算指向源字符串中指定起始位置 nPos 的指针。
for (i=0;i<nCount;i++) { //通过一个循环,尝试从 str 开始的位置复制 nCount 个字符到目标字符串 szDest
c=*(str+i); //从源字符串中读取当前位置的字符
if (c) { //如果读取的字符非空,则将其复制到目标字符串的对应位置;
*(szDest+i)=c;
}
else { //如果读取的字符为空,说明已经到达源字符串的末尾,将目标字符串的当前位置设置为空字符,并跳出循环。
*(szDest+i)='\0';
break;
}
}
*(szDest+nCount) ='\0'; //在目标字符串的末尾添加空字符,确保它是一个正确终止的字符串。
}