RTKLIB源码解析(三)、 Rinex文件读取(rinex.c)——1

目录

1、strstr()

2、strrchr() 和 strchr()

(1)strrchr()

(2) strchr()

3、strcmp()

4、定义一个指针数组,*ifile[MAXINFILE]

5、如果在VS中进行后处理,将ts、te设置为0,RTKLIB识别开始和结束时间在:

6、RTKLIB提供了Output Debug Trace (OFF Level1-5)

7、strstr(infile[i],"%r")

8、reppath函数:

9、向量内积(点乘)和外积(叉乘)

10、涉及的矩阵运算

11、setstr()

12、staticint cmpobs(const void *p1,const void *p2)

13、结构体中含有字符数组,用指向结构体的指针赋初值

14、getbits和getbitu函数

15、setbits和setbitu函数

16、adjweek()函数

17、adjweek()函数

18、timestr_rnx()函数

19、sat2code()函数

20、decode_obsh()函数

21、decode_navh()函数

22、readrnxh()函数

23、decode_obsepoch()函数

24、decode_obsdata()函数

(1)satid2no()函数

(2)sigind_t结构体

25、addobsdata()函数

(1)obsd_t结构体

(2)obs_t结构体

26、set_index()函数

(1)obs2code()函数



1、strstr()

C 库函数 char *strstr(const char *haystack, const char *needle) 在字符串 haystack 中查找第一次出现字符串 needle 的位置,不包含终止符 '\0'。

参考:C 库函数 - strstr()

#include <stdio.h>
#include <string.h>
int main() {
	char dest[] = "Hello A World";
	char sour[] = "A";
	char sour1 = 'A';
	char *temp;
	int  temp1;
	temp=strstr(dest, sour);
	printf("temp=%s\n",temp);
	temp = strrchr(dest, sour1);
	printf("temp=%s\n", temp);
	temp1 = strcmp(dest, sour);
	printf("temp=%d\n", temp1);
	return 0;
}

输出:

 如果没有查找到,返回null

	//测试strstr
	char dest[] = "Hello A World";
	char sour[] = "A";
	char sour1 = 'Z';
	char *Temp;
	int  temp1;
	Temp = strstr(dest, sour);
	printf("Temp=%s\n", Temp);
	Temp = strrchr(dest, sour1);
	printf("Temp=%s\n", Temp);
	temp1 = strcmp(dest, sour);
	printf("temp1=%d\n", temp1);

2、strrchr() 和 strchr()

(1)strrchr()

#include "string"  
char *strrchr( const char *str, int ch );

功能:函数返回一个指针,查找字符ch在指定字符串str从后面开始的第一次出现的位置,如果成功,则返回从该位置到字符串结尾的所有字符,如果失败,则返回 false。

<?php

    echo strrchr('Hello world!','world');

?>

输出: world!

extern int readpcv(const char *file, pcvs_t *pcvs)
{
    pcv_t *pcv;
    char *ext;
    int i,stat;
    
    trace(3,"readpcv: file=%s\n",file);
    /*char *strrchr( const char *str, int ch );
	功能:函数返回一个指针,它指向字符ch 在字符串str末次出现的位置,如果匹配失败,返回NULL。*/
    if (!(ext=strrchr(file,'.'))) ext="";
    
    if (!strcmp(ext,".atx")||!strcmp(ext,".ATX")) {
        stat=readantex(file,pcvs);
    }
    else {
        stat=readngspcv(file,pcvs);
    }

读取接收机和卫星天线文件中,使用!

(2) strchr()

char *strchr(const char *str, int c)
参数

  1.     str -- 要被检索的 C 字符串。
  2.     c -- 在 str 中要搜索的字符。

返回值
该函数返回在字符串 str 中第一次出现字符 c 的位置,如果未找到该字符则返回 NULL。

C 库函数 char *strchr(const char *str, int c) 在参数 str 所指向的字符串中搜索第一次/开始位置出现字符 c(一个无符号字符)的位置。

示例代码:

#include <stdio.h>
#include <string.h>
 
int main ()
{
   const char str[] = "http://www.runoob.com";
   const char ch = '.';
   char *ret;
 
   ret = strchr(str, ch);
 
   printf("|%c| 之后的字符串是 - |%s|\n", ch, ret);
   
   return(0);
}

运行结果:

|.| 之后的字符串是 - |.runoob.com|

3、strcmp()

#include <string.h>
  int strcmp( const char *str1, const char *str2 );

功能:比较字符串str1 和 str2, 返回值如下:

返回值

解释

less than 0

str1 is less than str2

equal to 0

str1 is equal to str2

greater than 0

str1 is greater than str2

 for (j = k = nf = 0; j < n; j++) {
 
	ext = strrchr(infile[j], '.');
 
	if (ext && (!strcmp(ext, ".rtcm3") || !strcmp(ext, ".RTCM3"))) {
		strcpy(ifile[nf++], infile[j]);
	}
	else {
		/* include next day precise ephemeris or rinex brdc nav */
		ttte = tte;
		if (ext && (!strcmp(ext, ".sp3") || !strcmp(ext, ".SP3") ||
			!strcmp(ext, ".eph") || !strcmp(ext, ".EPH"))) {
			ttte = timeadd(ttte, 3600.0);
		}
		else if (strstr(infile[j], "brdc")) {
			ttte = timeadd(ttte, 7200.0);
		}
		nf += reppaths(infile[j], ifile + nf, MAXINFILE - nf, tts, ttte, "", "");
	}
	while (k < nf) index[k++] = j;
 
	if (nf >= MAXINFILE) {
		trace(2, "too many input files. trancated\n");
		break;
	}
}

简单总结下三个函数:

 int strcmp( const char *str1, const char *str2 );
 char *strrchr( const char *str, int ch );
 char *strstr( const char *str1, const char *str2 );

看两点:str2参数不同,返回值不同;

strncpy()

C 库函数 char *strncpy(char *dest, const char *src, size_t n) 把 src 所指向的字符串复制到 dest,最多复制 n 个字符。当 src 的长度小于 n 时,dest 的剩余部分将用空字节填充。

https://www.runoob.com/cprogramming/c-function-strncpy.html

注:需要自己加上’\0‘;

4、定义一个指针数组,*ifile[MAXINFILE]

#define MAXINFILE   1000         /* max number of input files */
char *ifile[MAXINFILE]         //指针数组
for (i=0;i<n;i++) {
            if (!(ifile[i]=(char *)malloc(1024))) {
                free(base_); for (;i>=0;i--) free(ifile[i]);
                freepreceph(&navs,&sbss,&lexs);
                return 0;
            }
        }
/* free prec ephemeris and sbas data -----------------------------------------*/
static void freepreceph(nav_t *nav, sbs_t *sbs, lex_t *lex)
{
    int i;
    
    trace(3,"freepreceph:\n");
    
    free(nav->peph); nav->peph=NULL; nav->ne=nav->nemax=0;
    free(nav->pclk); nav->pclk=NULL; nav->nc=nav->ncmax=0;
    free(nav->fcb ); nav->fcb =NULL; nav->nf=nav->nfmax=0;
    free(nav->seph); nav->seph=NULL; nav->ns=nav->nsmax=0;
    free(sbs->msgs); sbs->msgs=NULL; sbs->n =sbs->nmax =0;
    free(lex->msgs); lex->msgs=NULL; lex->n =lex->nmax =0;
    for (i=0;i<nav->nt;i++) {
        free(nav->tec[i].data);
        free(nav->tec[i].rms );
    }
    free(nav->tec ); nav->tec =NULL; nav->nt=nav->ntmax=0;
    
    if (fp_rtcm) fclose(fp_rtcm);
    free_rtcm(&rtcm);
}

将指针数组中的每一个元素(指针),通过malloc函数,在堆区进行内存分配;如果分配任意一个没有分配成功,则free释放结构体变量(base_、ifile[i]、navs、sbss、lexs)

5、如果在VS中进行后处理,将ts、te设置为0,RTKLIB识别开始和结束时间在:

一般我们将其设置为0,如下所示:

gtime_t ts = { 0 }, te = { 0 };

当RTKLIB读取完obs、nav之后,

/* read obs and nav data -----------------------------------------------------*/
static int readobsnav(gtime_t ts, gtime_t te, double ti, char **infile,
                      const int *index, int n, const prcopt_t *prcopt,
                      obs_t *obs, nav_t *nav, sta_t *sta)

在上面函数中,有如下的代码片段,根据观测数据得到时间跨度,ts、te

    /* set time span for progress display */
    if (ts.time==0||te.time==0) {
        for (i=0;   i<obs->n;i++) if (obs->data[i].rcv==1) break;
        for (j=obs->n-1;j>=0;j--) if (obs->data[j].rcv==1) break;
        if (i<j) {
            if (ts.time==0) ts=obs->data[i].time;
            if (te.time==0) te=obs->data[j].time;
            settspan(ts,te);
        }
    }

6、RTKLIB提供了Output Debug Trace (OFF Level1-5)

如果在VS中我们打开了Debug Trace,那么函数内部开始运行及将相关调试信息保存到文件中;调试开始的代码位于:

位于:static int execses(……)

/* execute processing session ------------------------------------------------*/
static int execses(gtime_t ts, gtime_t te, double ti, const prcopt_t *popt,
                   const solopt_t *sopt, const filopt_t *fopt, int flag,
                   char **infile, const int *index, int n, char *outfile)
{
    FILE *fp;
    prcopt_t popt_=*popt;
    char tracefile[1024],statfile[1024],path[1024],*ext;
    
    trace(3,"execses : n=%d outfile=%s\n",n,outfile);
    
    /* open debug trace */
    if (flag&&sopt->trace>0) {
        if (*outfile) {
            strcpy(tracefile,outfile);
            strcat(tracefile,".trace"); //原输出文件名,后加.trace
        }
        else {
            strcpy(tracefile,fopt->trace);
        }
        traceclose();
        traceopen(tracefile); //开始对 trace文件进行写处理
        tracelevel(sopt->trace);
    }

 位于:static int execses(……)中

/* solution statistics level (0:off,1:states,2:residuals) */

输出结果统计文件:

    /* open solution statistics */
    if (flag&&sopt->sstat>0) {
        strcpy(statfile,outfile);
        strcat(statfile,".stat");
        rtkclosestat();
        rtkopenstat(statfile,sopt->sstat);
    }

7、strstr(infile[i],"%r")

C 库函数 char *strstr(const char *haystack, const char *needle) 在字符串 haystack 中查找第一次出现字符串 needle 的位置,不包含终止符 '\0'。

 readpreceph函数中,如下代码片段:

    /* read precise ephemeris files */
    for (i=0;i<n;i++) {
        if (strstr(infile[i],"%r")||strstr(infile[i],"%b")) continue;
        readsp3(infile[i],nav,0);
    }
    /* read precise clock files */
    for (i=0;i<n;i++) {
        if (strstr(infile[i],"%r")||strstr(infile[i],"%b")) continue;
        readrnxc(infile[i],nav);
    }
    /* read satellite fcb files */
    for (i=0;i<n;i++) {
        if (strstr(infile[i],"%r")||strstr(infile[i],"%b")) continue;
        if ((ext=strrchr(infile[i],'.'))&&
            (!strcmp(ext,".fcb")||!strcmp(ext,".FCB"))) {
            readfcb(infile[i],nav);
        }
    }

 如果输入文件中,含有基站和流动站的通配符:

 if (strstr(infile[i],"%r")||strstr(infile[i],"%b")) continue;

8、reppath函数:

/* replace keywords in file path -----------------------------------------------
* replace keywords in file path with date, time, rover and base station id
* args   : char   *path     I   file path (see below)
*          char   *rpath    O   file path in which keywords replaced (see below)
*          gtime_t time     I   time (gpst)  (time.time==0: not replaced)
*          char   *rov      I   rover id string        ("": not replaced)
*          char   *base     I   base station id string ("": not replaced)
* return : status (1:keywords replaced, 0:no valid keyword in the path,
*                  -1:no valid time)
* notes  : the following keywords in path are replaced by date, time and name
*              %Y -> yyyy : year (4 digits) (1900-2099)
*              %y -> yy   : year (2 digits) (00-99)
*              %m -> mm   : month           (01-12)
*              %d -> dd   : day of month    (01-31)
*              %h -> hh   : hours           (00-23)
*              %M -> mm   : minutes         (00-59)
*              %S -> ss   : seconds         (00-59)
*              %n -> ddd  : day of year     (001-366)
*              %W -> wwww : gps week        (0001-9999)
*              %D -> d    : day of gps week (0-6)
*              %H -> h    : hour code       (a=0,b=1,c=2,...,x=23)
*              %ha-> hh   : 3 hours         (00,03,06,...,21)
*              %hb-> hh   : 6 hours         (00,06,12,18)
*              %hc-> hh   : 12 hours        (00,12)
*              %t -> mm   : 15 minutes      (00,15,30,45)
*              %r -> rrrr : rover id
*              %b -> bbbb : base station id
*-----------------------------------------------------------------------------*/
extern int reppath(const char *path, char *rpath, gtime_t time, const char *rov,
                   const char *base)

如果输入文件(file)中,含有通配符,则 reppath函数的目的就是将file[]文件名中的通配符进行替换,保存到rpath中;这个根据输入的文件名是否含通配符而定!

9、向量内积(点乘)和外积(叉乘)

  • 向量的内积(点乘/数量积)

对两个向量执行点乘运算,就是对这两个向量对应位一一相乘之后求和的操作,如下所示,对于向量a和向量b:

 a和b的点积公式为:

这里要求一维向量a和向量b的行列数相同注意:点乘的结果是一个标量(数量而不是向量)

  • 向量的外积(叉乘)

概括地说,两个向量的外积,又叫叉乘、叉积向量积,其运算结果是一个向量而不是一个标量。并且两个向量的外积与这两个向量组成的坐标平面垂直。

定义:向量a与b的外积a×b是一个向量,其长度等于|a×b| = |a||b|sin∠(a,b),其方向正交于a与b。并且,(a,b,a×b)构成右手系。
特别地,0×a = a×0 = 0.此外,对任意向量a,a×a=0。

对于向量a和向量b:
 

 a和b的外积公式为:

 向量内积(点乘)和外积(叉乘)概念及几何意义 - 朝研行歌 - 博客园 (cnblogs.com)

RTKLIB中 求向量范数:先内积,然后开方

/* euclid norm  欧几里得范数,说白了,就是同一个向量点乘后开方,求向量长度-----------------------------------------------------------------
* euclid norm of vector
* args   : double *a        I   vector a (n x 1)
*          int    n         I   size of vector a
* return : || a ||
*-----------------------------------------------------------------------------*/
extern double norm(const double *a, int n)
{
    return sqrt(dot(a,a,n));
}

RTKLIB叉乘实现:

/* outer product of 3d vectors 叉积 外积-------------------------------------------------
* outer product of 3d vectors 
* args   : double *a,*b     I   vector a,b (3 x 1)
*          double *c        O   outer product (a x b) (3 x 1)
* return : none
*-----------------------------------------------------------------------------*/
extern void cross3(const double *a, const double *b, double *c)
{
    c[0]=a[1]*b[2]-a[2]*b[1];
    c[1]=a[2]*b[0]-a[0]*b[2];
    c[2]=a[0]*b[1]-a[1]*b[0];
}

10、涉及的矩阵运算

 #include <string.h>
  void *memcpy( void *to, const void *from, size_t count );

 功能:函数从from中复制count 个字符到to中,并返回to指针。 如果to 和 from 重叠,则函数行为不确定。

/* copy matrix -----------------------------------------------------------------
* copy matrix 将B矩阵的值,复制到A中,所以传入两个矩阵
* args   : double *A        O   destination matrix A (n x m)
*          double *B        I   source matrix B (n x m)
*          int    n,m       I   number of rows and columns of matrix
* return : none
*-----------------------------------------------------------------------------*/
extern void matcpy(double *A, const double *B, int n, int m)
{
    memcpy(A,B,sizeof(double)*n*m);
}

11、setstr()

函数将 src字符串中长度为n的字串,复制给dst,长度必须小于strlen(dst),但是无论n为多少,dst的内容都被覆盖,详情见示例!

/* set string without tail space ---------------------------------------------*/
static void setstr(char *dst, const char *src, int n)
{
    char *p=dst;
    const char *q=src;
    while (*q&&q<src+n) *p++=*q++;
    *p--='\0';
    while (p>=dst&&*p==' ') *p--='\0';
}

示例程序:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
/* set string without tail space ---------------------------------------------*/
static void setstr(char *dst, const char *src, int n)
{
	char *p = dst;
	const char *q = src;
	while (*q&&q < src + n) *p++ = *q++;
	*p-- = '\0';
	while (p >= dst && *p == ' ') *p-- = '\0';
}
int main()
{
	char A[] = "VVVVVVVV";
	char B[] = "Am er ican Welcome";
	setstr(A,B,strlen(A));
	printf("A[]=%s\n", A);
	printf("B[]=%s\n", B);
	char C[] = "VVVVVVVV";
	char D[] = "Am er ican Welcome";
	setstr(C, D, 5);
	printf("C[]=%s\n",C);
	printf("D[]=%s\n",D);
	return 0;
}

运行结果:

12、staticint cmpobs(const void *p1,const void *p2)

其中cmpobs是C标准库的函数;函数的定义如下:

功能: 使用快速排序例程进行排序
头文件:stdlib.h
用法:  void qsort(void* base, size_t num, size_t width, int(__cdecl*compare)(const void*,const void*));
参数:

  • 待排序数组,排序之后的结果仍放在这个数组中
  • 数组中待排序元素数量
  • 各元素的占用空间大小(单位为字节)
  • 指向函数的指针,用于确定排序的顺序(需要用户自定义一个比较函数)

 代码如下:

qsort(obs->data,obs->n,sizeof(obsd_t),cmpobs);
 
/* compare observation data -------------------------------------------------*/
static int cmpobs(const void *p1, const void *p2)
{
    obsd_t *q1=(obsd_t *)p1,*q2=(obsd_t *)p2;
    double tt=timediff(q1->time,q2->time);
    if (fabs(tt)>DTTOL) return tt<0?-1:1;
    if (q1->rcv!=q2->rcv) return (int)q1->rcv-(int)q2->rcv;
    return (int)q1->sat-(int)q2->sat;
}

13、结构体中含有字符数组,用指向结构体的指针赋初值

结构体sta_t里面有 字符数组、int 、double类型;因此通过指向sta_t结构体类型的指针,对结构体进行初始化(赋初值)时,字符数组的名字是个地址,也即看作指针;因此,

*sta->name='\0';  //成员数组的名字是个地址,需要使用”*“解引用,即把‘\0’赋值给这个数组的第0个成员(首位置处)

或一般把字符串赋值给一个结构体成员,使用memcpy

示例代码:
 

#include <iostream>
using namespace std;
#define  MAXANT 1024
 
typedef struct {        /* station parameter type */
	char name[MAXANT]; /* marker name */
	char marker[MAXANT]; /* marker number */
	char antdes[MAXANT]; /* antenna descriptor */
	char antsno[MAXANT]; /* antenna serial number */
	char rectype[MAXANT]; /* receiver type descriptor */
	char recver[MAXANT]; /* receiver firmware version */
	char recsno[MAXANT]; /* receiver serial number */
	int antsetup;       /* antenna setup id */
	int itrf;           /* ITRF realization year */
	int deltype;        /* antenna delta type (0:enu,1:xyz) */
	double pos[3];      /* station position (ecef) (m) */
	double del[3];      /* antenna position delta (e/n/u or x/y/z) (m) */
	double hgt;         /* antenna height (m) */
} sta_t;
/* initialize station parameter ----------------------------------------------*/
static void init_sta(sta_t *sta)
{
	int i;
	/* 	*/
	*sta->name = '\0'; // -> 优先级 高于 *
	*sta->marker = '\0';
	*sta->antdes = '\0';
	*sta->antsno = '\0';
	*sta->rectype = '\0';
	*sta->recver = '\0';
	*sta->recsno = '\0';
 
	sta->antsetup = sta->itrf = sta->deltype = 0;
	for (i = 0; i < 3; i++) sta->pos[i] = 0.0;
	for (i = 0; i < 3; i++) sta->del[i] = 0.0;
	sta->hgt = 0.0;
}
int main() {
 
	sta_t Sta;
	sta_t *sta = &Sta;
	if (sta) init_sta(sta);  /* station parameter type */
	/*
	char str[MAXANT];
	*str = '\0';
	*/
	/*
	char *p_str = str;
	p_str = (char *)"字符数组";
	*/
	system("pause");
	return 0;
}

14、getbits和getbitu函数

/* extract unsigned/signed bits ------------------------------------------------
* extract unsigned/signed bits from byte data
* args   : unsigned char *buff I byte data
*          int    pos    I      bit position from start of data (bits)
*          int    len    I      bit length (bits) (len<=32)
* return : extracted unsigned/signed bits
*-----------------------------------------------------------------------------*/
extern unsigned int getbitu(const unsigned char *buff, int pos, int len)
{
    unsigned int bits=0;
    int i;
    for (i=pos;i<pos+len;i++) bits=(bits<<1)+((buff[i/8]>>(7-i%8))&1u); //1U 表示无符号整型1
    return bits;
}
extern int getbits(const unsigned char *buff, int pos, int len)
{
    unsigned int bits=getbitu(buff,pos,len);
    if (len<=0||32<=len||!(bits&(1u<<(len-1)))) return (int)bits;
    return (int)(bits|(~0u<<len)); /* extend sign */
}

都是从多少位里面获取数据,getbitu是获取无符号32位的数据;getbits是获取32位有符号的数据。其中,POS为起始位,len为位数长度;

示例代码:

#include <stdio.h>
/* extract unsigned/signed bits ------------------------------------------------
* extract unsigned/signed bits from byte data
* args   : unsigned char *buff I byte data
*          int    pos    I      bit position from start of data (bits)
*          int    len    I      bit length (bits) (len<=32)
* return : extracted unsigned/signed bits
*-----------------------------------------------------------------------------*/
extern unsigned int getbitu(const unsigned char *buff, int pos, int len)
{
	unsigned int bits = 0;
	int i;
	for (i = pos; i < pos + len; i++) bits = (bits << 1) + ((buff[i / 8] >> (7 - i % 8)) & 1u); //1U 表示无符号整型1
	//printf("getbitu 函数 bits=%d\n", bits);
	return bits;
	
}
extern int getbits(const unsigned char *buff, int pos, int len)
{
	unsigned int bits = getbitu(buff, pos, len);
	if (len <= 0 || 32 <= len || !(bits&(1u << (len - 1)))) return (int)bits;
	 
	return (int)(bits | (~0u << len)); /* extend sign */
}
/* get signed 38bit field ----------------------------------------------------*/
static double getbits_38(const unsigned char *buff, int pos)
{
	return (double)getbits(buff, pos, 32)*64.0 + getbitu(buff, pos + 32, 6);
}
int main() {
	//1005数据(基站位置)
	unsigned char str[] = { 0XD3, 0X00, 0X13, 0X3E, 0XD7, 0XD3, 0X02, 0X02, 0X98, 0X0E, 0XDE, 0XEF, 0X34, 0XB4, 0XBD, 0X62, 0XAC, 0X09, 0X41, 0X98, 0X6F, 0X33, 0X36, 0X0B, 0X98 };
	int i = 24, j, staid, itrf,messageID;
	double rr[3], re[3], pos[3];
	
	messageID= getbitu(str, i, 12); i += 12; //信息类型
	staid = getbitu(str, i, 12); i += 12; //基准站ID
	itrf = getbitu(str, i, 6); i += 6 + 4; //
	rr[0] = getbits_38(str, i); i += 38 + 2; //天线位置X (ECEF坐标)
	rr[1] = getbits_38(str, i); i += 38 + 2; //天线位置Y (ECEF坐标)
	rr[2] = getbits_38(str, i);   //天线位置Z (ECEF坐标)
 
	printf("messageID=%d staid =%d\n", messageID, staid);
	printf("ECEF下天线位置:%d %d %d\n", rr[0], rr[1], rr[2]);
 
	system("pause");
	return 0;
}

运行结果:

15、setbits和setbitu函数

/* set unsigned/signed bits ----------------------------------------------------
* set unsigned/signed bits to byte data
* args   : unsigned char *buff IO byte data
*          int    pos    I      bit position from start of data (bits)
*          int    len    I      bit length (bits) (len<=32)
*         (unsigned) int I      unsigned/signed data
* return : none
*-----------------------------------------------------------------------------*/
extern void setbitu(unsigned char *buff, int pos, int len, unsigned int data)
{
    unsigned int mask=1u<<(len-1);
    int i;
    if (len<=0||32<len) return;
    for (i=pos;i<pos+len;i++,mask>>=1) {
        if (data&mask) buff[i/8]|=1u<<(7-i%8); else buff[i/8]&=~(1u<<(7-i%8));
    }
}
extern void setbits(unsigned char *buff, int pos, int len, int data)
{
    if (data<0) data|=1<<(len-1); else data&=~(1<<(len-1)); /* set sign bit */
    setbitu(buff,pos,len,(unsigned int)data);
}

16、adjweek()函数

函数功能:是用来调整t与t0的周数在同一周

static gtime_t adjweek(gtime_t t, gtime_t t0)
{
    double tt=timediff(t,t0);
    if (tt<-302400.0) return timeadd(t, 604800.0);
    if (tt> 302400.0) return timeadd(t,-604800.0);
    return t;
}

这个函数的返回值和参数都是gtime_t类型的,在rtklib.h文件中查看gtime_t类型,可以看到这个类型是含有一个time_t类型数据和一个double类型的数据。

time_t是从一个时间点(一般是1970年1月1日0时0分0秒)到某个时间的秒数,但是是长整型的数据。double类型的那个数据是来表示不足一秒的小数部分。也就是说,time_t类型合起来表示从1970年1月1日0时0分0秒到某个时刻的准确时间。

17、adjweek()函数

函数功能:是用来调整t与t0的周数在同一天

static gtime_t adjday(gtime_t t, gtime_t t0)
{
    double tt=timediff(t,t0);
    if (tt<-43200.0) return timeadd(t, 86400.0);
    if (tt> 43200.0) return timeadd(t,-86400.0);
    return t;
}

18、timestr_rnx()函数

函数功能:获取系统的时间,将时间结果按照yyyymmdd hhmmss UTC的格式存入str字符串中。

static void timestr_rnx(char *str)
{
    gtime_t time;
    double ep[6];
    time=timeget();
    time.sec=0.0;
    time2epoch(time,ep);
    sprintf(str,"%04.0f%02.0f%02.0f %02.0f%02.0f%02.0f UTC",ep[0],ep[1],ep[2],
            ep[3],ep[4],ep[5]);
}

19、sat2code()函数

函数功能:是将各种卫星系统(GPS,BEIDOU,GALILEO,GLONASS等卫星)的卫星种类和prn号一起转化为特定的字符串,以供后续使用。这个函数中主要就有一个函数。

static int sat2code(int sat, char *code)
{
    int prn;
    switch (satsys(sat,&prn)) {
        case SYS_GPS: sprintf(code,"G%2d",prn-MINPRNGPS+1); break;
        case SYS_GLO: sprintf(code,"R%2d",prn-MINPRNGLO+1); break;
        case SYS_GAL: sprintf(code,"E%2d",prn-MINPRNGAL+1); break;
        case SYS_SBS: sprintf(code,"S%2d",prn-100); break;
        case SYS_QZS: sprintf(code,"J%2d",prn-MINPRNQZS+1); break;
        case SYS_CMP: sprintf(code,"C%2d",prn-MINPRNCMP+1); break;
        default: return 0;
    }
    return 1;
}

20、decode_obsh()函数

static void decode_obsh(FILE *fp, char *buff, double ver, int *tsys,
                        char tobs[][MAXOBSTYPE][4], nav_t *nav, sta_t *sta)

在介绍 decode_obsh 函数之前,先说明一下其中的一个子函数

extern double str2num(const char *s, int i, int n)

str2num 函数功能:

s是指定的字符串,i是从s中第i个字符开始,一直截取n位,然后将截取的字符串转化为数值。这个函数是将字符串转化为double类型的数值的函数。里面利用sscanf函数实现。

举个例子,例如“123E01”这个字符串会被转化为1230。但是只有其中的字母是E的时候才象征着科学计数法。星历中使用D表示科学计数法,所以在函数中先将D替换为E,然后进行转化。
 

上面的图片是观测值头文件的一部分。参照RINEX说明文档,可以知道在文件头的每一行的前60个字节是值,60个字节后面的部分是相应的标签说明。各种标签对应的意义在RINEX说明文档里面已经详细说明。

decode_obsh 函数功能:
按照字节数读取我们需要的值。将读取到的观测系统的观测类型放入tobs这个三维数组中,将关于测站的信息(测站名字,大概位置等等)放到sta中;至于nav这个参数只是为了方便观测值中含有GLONASS观测值的文件使用的。
 

21、decode_navh()函数

static void decode_navh(char *buff, nav_t *nav)

函数功能:

将卫星星历文件中的文件头按照字节将buff中的各个参数读进nav中,提取出有用的信息

22、readrnxh()函数

static int readrnxh(FILE *fp, double *ver, char *type, int *sys, int *tsys,
                    char tobs[][MAXOBSTYPE][4], nav_t *nav, sta_t *sta)

函数功能:文件头数据读取
步骤
主要利用fgets()逐行读取并缓存文件流。
(1)先定义缓存字符数组buff和用于比较的指针*label:char buff[MAXRNXLEN],*label=buff+60; 其中buff缓存一行的所有字符,*lable指向第60个字符的位置;
(2)通过每行特有的字符串与lable比较,判断目前读取的位置在哪一行,进而读取本行其他参数。
如:第一行特有字符串:“RINEX VERSION / TYPE”,通过该字符串定位到第一行,进而获取版本号“2.10”与文件类型“n"/“o”。
(3)遍历文件头,依次获取卫星系统、电离层改正参数(ION ALPHA、ION BETA)、UTC时转换至GPS时的参数(A0,A1,T,W)及闰秒等信息。直到读取”END OF HEADER“字符串为止 .

23、decode_obsepoch()函数

static int decode_obsepoch(FILE *fp, char *buff, double ver, gtime_t *time,
                           int *flag, int *sats)

函数功能:

这个函数是将每个观测历元的时间提取出来放入time参数中,也就是将下面的图片中的内容放入time中。

24、decode_obsdata()函数

static int decode_obsdata(FILE *fp, char *buff, double ver, int mask,
                          sigind_t *index, obsd_t *obs)

先介绍一下这个函数里面的子函数:

(1)satid2no()函数

extern int satid2no(const char *id)

这个函数是位于rtkcnm.c中的一个函数。这个函数的输入参数id是根据卫星系统的类型加上卫星的prn号返回一个卫星的整数标记。举个例子吧,例如id是“R09”,那么返回的值就是32+9=41,反过来,后面还会有专门能将41转化为“R09”的函数。也就是说,在rtklib中,卫星系统连同卫星号被设计成一个连续的整数。依照依次累加的算法进行转化。也就是说在卫星号传递的时候,可以直接传递整数,而不用传递一个字符串。这样无论是什么卫星系统的卫星都对应唯一一个整数。会大大降低运行时间,不得不说,rtklib无论是在代码架构上还是在效率上都近乎于达到了当时的最高水平。

(2)sigind_t结构体

typedef struct {                        /* signal index type */
    int n;                              /* number of index */
    int frq[MAXOBSTYPE];                /* signal frequency (1:L1,2:L2,...) */
    int pos[MAXOBSTYPE];                /* signal index in obs data (-1:no) */
    unsigned char pri [MAXOBSTYPE];     /* signal priority (15-0) */
    unsigned char type[MAXOBSTYPE];     /* type (0:C,1:L,2:D,3:S) */
    unsigned char code[MAXOBSTYPE];     /* obs code (CODE_L??) */
    double shift[MAXOBSTYPE];           /* phase shift (cycle) */
} sigind_t;

第一次看这个结构体是懵圈的,因为我不知道这个是干什么的,也不知道这个东西是怎么来的。在“瞻前顾后”了好长时间才发现这个的用途。其实这个和我们文件头中的观测值类型有着很大的关系,也就是和下面的图片上的东西有着很大的关系。


 这个是在.o文件的文件头中,表示每种卫星系统的载波类型和观测值类型。sigind_t就是根据这个建立起来的。
在下面的代码中就会发现,每种类型的系统其实对应的就是一个sigind_t结构体,也就是说只需要建立六个结构体就够了。
结构体中的n代表这个卫星系统总的观测值类型,对应的卫星系统标识符后面的数字,例如G后面的16,R后面的12等等。剩下的观测值类型就拆开存在结构体中的相应位置。
总之sigind_t结构体就像一个索引一样,告诉主函数每个卫星系统的观测值类型是什么,使用了哪种频率的载波,采用了哪种测距码观测的等等信息。

下面回到 decode_obsdata函数
判断观测值文件是否是3.0以上的版本,如果是3.0以上版本,就将每个历元下面的观测值按照一行一行的提取出来,每次提取一行,根据前三位判断卫星系统和prn号。这里的prn号就是连续的,也就是其中既包含了卫星系统的信息,也包含了卫星的真实prn号。
对应不同的系统采用相应的索引,根据版本号来读取相应的观测值数据暂时存在val中,LLI信息存在lli中,最后将这两个数组中的信息存放在obs结构体中。
最后的存放过程是这样的。
 

/* save obs data */
    for (i=0;i<ind->n;i++) {
        if (p[i]<0||val[i]==0.0) continue;
        switch (ind->type[i]) {
            case 0: obs->P[p[i]]=val[i]; obs->code[p[i]]=ind->code[i]; break;
            case 1: obs->L[p[i]]=val[i]; obs->LLI [p[i]]=lli[i];       break;
            case 2: obs->D[p[i]]=(float)val[i];                        break;
            case 3: obs->SNR[p[i]]=(unsigned char)(val[i]*4.0+0.5);    break;
        }
    }

上面代码中的 obs->P 代表着这个观测值结构体中的伪距观测值。不管是伪距观测值还是载波相位观测值和多普勒观测值,都是利用各种载波得到的。

这里还是举个例子吧,载波一般的情况下有三种频率,所以无论是obs->P,还是obs->L和obs->D,在没有扩展载波的情况下都是大小为3的数组。obs->P[0]就是利用L1载波观测到的伪距,obs->P[1]就是利用L2载波观测到的伪距…大家以此类推就行了。那么这个函数就完成了。

25、addobsdata()函数

在了解这个函数之前,应该先说这个函数中对应的几个结构体。

(1)obsd_t结构体

这个结构体是在rtklib.h中定义的一个用来存储某个历元中的某个卫星的观测值的。定义如下:

typedef struct {        /* observation data record */
    gtime_t time;       /* receiver sampling time (GPST) */
    unsigned char sat,rcv; /* satellite/receiver number */
    unsigned char SNR [NFREQ+NEXOBS]; /* signal strength (0.25 dBHz) */
    unsigned char LLI [NFREQ+NEXOBS]; /* loss of lock indicator */
    unsigned char code[NFREQ+NEXOBS]; /* code indicator (CODE_???) */
    double L[NFREQ+NEXOBS]; /* observation data carrier-phase (cycle) */
    double P[NFREQ+NEXOBS]; /* observation data pseudorange (m) */
    float  D[NFREQ+NEXOBS]; /* observation data doppler frequency (Hz) */
} obsd_t;

这里面的数组名称代表观测值类型,数组的下标代表载波类型。其中的数组名称如果看过RINEX的格式说明,一眼就能知道代表的意思。

(2)obs_t结构体

typedef struct {        /* observation data */
    int n,nmax;         /* number of obervation data/allocated */
    obsd_t *data;       /* observation data records */
} obs_t;

这个结构体中存储的是所有的观测值数据,n代表实际存储的观测值结构体的个数,nmax代表的是最多存储多少个观测值结构体,如果超过最大个数,需要重新开辟内存空间。在C++中,内存报错是比较讨厌的一类错误。在rtklib中,我们会发现其中的异常捕捉机制运用的非常好,几乎是一步一trace,这也让我学到了需要利用trace语句来处理异常,以便调试代码。

addobsdata()函数功能:

了上面的结构体之后,就可以知道这个函数就是添加新的观测值

26、set_index()函数

static void set_index(double ver, int sys, const char *opt,
                      char tobs[MAXOBSTYPE][4], sigind_t *ind)

函数的参数意义如下

ver        //RINEX文件版本号
sys        //卫星所属系统
opt        //选项
tobs       //观测值类型数组
ind        //得到的索引

其中,观测值类型数组是一个比较容易困惑的地方。之前我们用过这样的一个数组tobs[][MAXOBSTYPE][4],这个是在读取RIENX观测值文件的文件头得到的。里面存储了下面的内容

 还是举个例子来说明吧,针对于数组tobs[][MAXOBSTYPE][4]来说,tobs[0]意味着一个指向二位数组的指针,而0代表的是GPS系统,因此tobs[0]代表的是GPS系统的观测值类型。再看tobs[0][0],这个就是指向一个一维数组的指针,代表着GPS系统的第一个观测值类型,也就是说tobs[0][0]="C1C ",注意,一定是一个四位的字符串,因为在RINEX的格式说明中,每一个观测值类型占四位,包括了最后的一个空格。也就是说tobs[0][0]代表着GPS系统中利用L1载波上的C/A码测得的伪距。这下应该理解了吧。
 

tobs[][MAXOBSTYPE][4]             //以前用的
tobs[MAXOBSTYPE][4]               //本函数中的

对比上面两个可以发现,数组维数少了一维,但是由于函数的参数中已经指明了系统,所以就可以将三维数组退化成为了二维数组了

下面看这个函数中用了哪些子函数:

(1)obs2code()函数

这个函数是位于 rtkcnm.c 中的一个函数,代码长度不长,但是可能比较难理解。先看主函数中调用obs2code函数的语句吧。

ind->code[i]=obs2code(tobs[i]+1,ind->frq+i);

参数 tobs[i]+1,这个很考验C语言的指针方面的功底了。由于tobs是一个二维数组,因此 tobs[i]+1 应该表示的是二维数组中的第i+1个字符串向后舍弃第一位后的子串。说起来很拗口,举个例子就好了。比如对于GPS系统来说,tobs[0]+1 就是“C1C "中的"1C "子串。也就是传递了子串和索引中的频率。由于索引中的频率尚未有值,因此可以推断出来,这个函数一定是根据tobs[i]+1 来为索引中的频率赋值。
下面来看 obs2code的函数体:

extern unsigned char obs2code(const char *obs, int *freq)
{
    int i;
    if (freq) *freq=0;
    for (i=1;*obscodes[i];i++) {
        if (strcmp(obscodes[i],obs)) continue;
        if (freq) *freq=obsfreqs[i];
        return (unsigned char)i;
    }
    return CODE_NONE;
}

这个里面的obscodes和obsfreqs都是在rtkcnm.c的文件开头定义的数组:

static char *obscodes[]={       /* observation code strings */
    
    ""  ,"1C","1P","1W","1Y", "1M","1N","1S","1L","1E", /*  0- 9 */
    "1A","1B","1X","1Z","2C", "2D","2S","2L","2X","2P", /* 10-19 */
    "2W","2Y","2M","2N","5I", "5Q","5X","7I","7Q","7X", /* 20-29 */
    "6A","6B","6C","6X","6Z", "6S","6L","8L","8Q","8X", /* 30-39 */
    "2I","2Q","6I","6Q","3I", "3Q","3X",""              /* 40-49 */
};
static unsigned char obsfreqs[]={ /* 1:L1,2:L2,3:L5,4:L6,5:L7,6:L8,7:L3 */
    
    0, 1, 1, 1, 1,  1, 1, 1, 1, 1, /*  0- 9 */
    1, 1, 1, 1, 2,  2, 2, 2, 2, 2, /* 10-19 */
    2, 2, 2, 2, 3,  3, 3, 5, 5, 5, /* 20-29 */
    4, 4, 4, 4, 4,  4, 4, 6, 6, 6, /* 30-39 */
    2, 2, 4, 4, 3,  3, 3, 0        /* 40-49 */
};

也就是说,obscodes 中定义的全部是卫星观测值类型的后两个字母。再看obs2code函数体,可以发现,这个函数的作用就是在所有的观测值类型的后两位字母中找到传入的观测值类型。然后将对应的载波赋值给索引中的频率值,最后返回的是唯一确定的位置。


回到主函数,主函数中虽然有很多语句,但是我认为最终要的就是下面的语句:

for (i=n=0;*tobs[i];i++,n++) {
        ind->code[i]=obs2code(tobs[i]+1,ind->frq+i);
        ind->type[i]=(p=strchr(obscodes,tobs[i][0]))?(int)(p-obscodes):0;
        ind->pri[i]=getcodepri(sys,ind->code[i],opt);
        ind->pos[i]=-1;
    }

static const char obscodes[]="CLDS";

 函数总结

上面说了好多,总而言之,这个函数就是建立索引的一个函数,索引就是将o文件头中的观测值类型分割开来,分别存入不同的数组中,这些数组合起来构成了索引。


 

感谢:

rtklib中常用的几个函数&必须注意函数_wuwuku123的博客-CSDN博客_rtklib函数

学习rtklib(一)_WHU-学渣的博客-CSDN博客_rtklib学习

学习rtklib(二)_WHU-学渣的博客-CSDN博客

学习rtklib(三)_WHU-学渣的博客-CSDN博客_学习rtklib

  • 6
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

他人是一面镜子,保持谦虚的态度

你的鼓励是我最大的动力

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

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

打赏作者

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

抵扣说明:

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

余额充值