精密单点定位软件GAMP代码架构(1)

目录

1.进入main()函数

2.处理配置文件函数:proccfgfile(cfgfile)

3.batchProc()函数:处理一个文件夹的下的多个.o观测值文件

4.procOneFile()函数:逐个处理单个.o观测值文件

5.preProc()函数:获取该观测值文件的开始和结束时间

6.getObsInfo()函数:获取观测值信息

6.xStrMid()函数:


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.包括预处理、读取配置、检查参数、自动匹配文件选项、进行定位处理以及后处理。它还计算并打印程序的运行时间。需要注意的是,代码中有一些未定义的函数和全局变量,如 preProcreadcfgFilegampPospostProc 以及 PPP_Glo,这些应该是在其他地方定义的。此外,OUTWINOUTFILOFILE_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'; //在目标字符串的末尾添加空字符,确保它是一个正确终止的字符串。
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code_ADing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值