RINEX observation value file reading (O file)

Because the observation output formats of receivers from different manufacturers are inconsistent, in order to facilitate data exchange, a unified standard format has been formulated, called RINEX format (Receiver Independent Exchange, RINEX) .

This article first introduces the format of the observation value file of RINEX2.1.1, and then explains how to read data from the perspective of programming based on C language. Each line of code has detailed comments and explanations, hoping to bring help to surveying and mapping students .

Table of contents

Observation file (Observation)

O file header

O file data block 

Read O file program design

structure declaration 

O file read

Get O file epoch number (get_epochnum)

Convert string to floating point number (strtonum)

O file header read

O file data block read


How to download the observation file data is explained in N file reading , so I won't go into details here.

Observation file (Observation)

An observation file consists of a header and data blocks:

The table header includes: RINEX version number, receiver antenna number, station name, point number, observation type, sampling interval, start time, end time, etc.

The observation data block includes: observation time, pseudo-range observation value and phase observation value of each satellite, and some receivers also output Doppler, signal-to-noise ratio and so on. 

O file header

Example:

 The above is the GPS observation value file of version 2.11. The part marked in red box is the main data read in the pseudo-range positioning part. (Of course, considering the portability of the program, all the content should be read in full), and the header file will be explained later.

  • The first line : the version of rinex, the type of observation value, and the satellite system to which the observation data belongs
  • Second line : the name of the program used by the file, the name of the file unit, the date of the file
  • Lines 3 and 4 : Comments: Explanation for the selected system
  • The fifth line : the name of the antenna logo (roll call)
  • The sixth line : the number of roll call
  • The seventh line : the name of the observer, the name of the observation unit
  • Eighth line : receiver serial number, receiver type, receiver version number
  • Ninth line : antenna serial number, antenna type
  • Line 10 : Approximate position X, Y, Z of the measuring station
  • Line 11 : Antenna height, antenna center relative to station mark in east direction offset, north direction offset
  • Line 12 : Default L1 and L2 carrier wavelength factors
  • Line 13 : Number of observation types, type of observation
  • Line 14: The epoch interval of the observations
  • Lines 15 to 25 : Comments
  • Line 26 : Observation start time, system
  • Line 27 : end of file header

O file data block 

Example:

Epoch information:

The first line of the data block records the UTC time of the epoch, year, month, day, hour, minute, and second, followed by an epoch flag of an integer unit, which records the status of the epoch (0 means normal), and then is the number of satellites observed in the epoch, and the PRN number of each satellite.

When the number of observed satellites is >12, the information in one line will automatically wrap if it cannot be stored, as shown in the figure below:

When the number of observed satellites is >24, the line is changed again, and the PRN number of the satellite is aligned with the previous line.

The above two cases are intercepted from multiple systems. For a single system, the number of satellite observations per epoch > 10 is high-quality observations. So you only need to consider the case where the number of satellites is > 12. Of course, for the portability of the program, it is no problem to write it perfectly.

Data block information:

The next line of the epoch information is the data block that records the observation value. It takes each satellite as a unit and arranges it from left to right according to the type and order of the observation value in the header file. Each line records 5 observation values. When one line is not enough Turn down. When all satellite data has been recorded, go to the next epoch.

Read O file program design

structure declaration 

The creation of the O file structure is divided into three parts: the O file header structure, the epoch information structure and the data block structure.

For the convenience of calling later, when creating a structure, it is best to use typedef to rename the structure.

After renaming with typedef, when creating a structure variable or structure pointer, use the right side of the following equation: 

  • struct obs_head  =  obs_head
  • sturct obs_head* =  pobs_head

In the time system, the year, month, day, hour, and minute are all recorded in integers, and the seconds are recorded in decimals. 

O file header structure:

//观测值头文件
typedef struct obs_head
{
	double ver;//RINEX文件版本号
	char type[30];//文件类型
	double apX;//测站近似位置XYZ(WGS84)
	double apY;
	double apZ;
	double ANTH;//天线高
	double ANTdeltaE;//天线中心对于测站标志在东方向上的偏移量
	double ANTdeltaN;//天线中心对于测站标志在北方向上的偏移量
	int WAFL1;//缺省的L1载波的波长因子
	int WAFL2;//缺省的L2载波的波长因子
	int WAFflag;
	int obstypenum;//观测值类型数量
	char obstype[15];//rinex2.0观测值类型列表
	double interval;//观测值的历元间隔
	int f_y;//第一个观测记录的时刻
	int f_m;
	int f_d;
	int f_h;
	int f_min;
	double f_sec;
	char tsys[5];//时间系统
}obs_head,*pobs_head;

O file epoch information structure 

//观测值历元数据结构体
typedef struct obs_epoch
{
	//观测历元时刻
	int y;
	int m;
	int d;
	int h;
	int min;
	double sec;
	int p_flag;//历元标志
	int sat_num;//当前历元所观测到的卫星数量
	int sPRN[24];//当前历元所能观测到的卫星的PRN列表
}obs_epoch,*pobs_epoch;

 O file data block structure

//观测值数据结构体
typedef struct obs_body
{
	double obs[24][15];//观测值
}obs_body,*pobs_body;

Here 24 corresponds to the maximum number of satellites observed at the same time, and 15 corresponds to 15 types of observation values. For a single system, this is a very safe number, and overflow will not occur.

O file read

The idea of ​​​​reading the file is:

  • Create a file pointer, open the file
  • How many epochs to read the O file from the beginning of the data block
  • Create pointers to O file headers and O file data blocks respectively, and allocate memory for them.
  • Read the data row by row
  • close file

 The specific code is as follows:

//数据读取
FILE* fp_obs = NULL;//观测值文件指针
pobs_head obs_h = NULL;
pobs_epoch obs_e = NULL;
pobs_body obs_b = NULL;//创建结构体指针,并将其初始化为空
//O文件读取
	fp_obs = fopen("abpo0480.15o", "r");//以只读的方式打开O文件
	int o_epochnum = get_epochnum(fp_obs);//获取O文件历元数
	rewind(fp_obs);
	obs_h = (pobs_head)malloc(sizeof(obs_head));//给O文件头开辟空间
	obs_e = (pobs_epoch)malloc(sizeof(obs_epoch) * o_epochnum);
	obs_b = (pobs_body)malloc(sizeof(obs_body) * o_epochnum);
	if (obs_h && obs_e && obs_b)
	{
		read_h(fp_obs, obs_h);//读取O文件头
		read_b(fp_obs, obs_e, obs_b, obs_h->obstypenum);//读取O文件数据块
	}
	fclose(fp_obs);//关闭O文件

In the above code, get_epochnum is a self-created function to obtain the epoch number of the O file, and read_h and read_b are also created functions, which read the file header and read the data block respectively.

Get O file epoch number (get_epochnum)

Use the fgets function to read the file line by line:

The fegts function needs to include the header file <stdio.h>, which can be used as follows:

  • char * fgets ( char * str, int num, FILE * stream );
  • Input a pointer of char* type (used to store the read data), the number of read, file pointer, and its return type is char*, which is the new position after reading.
#define MAXRINEX 84//最大读取字符数

//获取O文件历元数
extern int get_epochnum(FILE* fp_obs)
{
	int n = 0;//记录历元数
	int satnum = 0;//记录每个历元的卫星数
	char flag;//存放卫星标志符号'G'
	char buff[MAXRINEX];//存放读取的字符串
	while (fgets(buff, MAXRINEX, fp_obs))
	{
		satnum = (int)strtonum(buff, 30, 2);
		strncpy(&flag, buff + 32, 1);
		if (flag == 'G')
		{
			n++;
		}
		//当卫星数超过12个时,一行存不下会转到下下一行,并且与上一行对齐
        //所以当出现这种情况时,要减1
		if (flag == 'G' && satnum > 12)
		{
			n--;
		}
	}
	return n;
}

In the above code:

  • n is the number of recorded epochs, which is also the final return value
  • MAXRINEX is the maximum number of read characters defined by the macro. The maximum number of characters in a line in the RINEX file is 81. Here, 84 is set and 3 characters are reserved.
  • satnum is the number of satellites in each epoch, used to judge whether there are more than 12 satellites, and the result needs to be reduced by 1 if there is a line break
  • buff is used to temporarily store the data read by fgets
  • falg is used to store the 32nd character, that is, the 'G' character of the first satellite. If the 32nd character of the line is 'G', it indicates that the line is the beginning of the epoch, and the result is counted

Convert string to floating point number (strtonum)

When using the fgets function to read data, we create a buff string to receive it. For information that happens to be a string type, such as the observation type tpye, we can use the strncpy function to copy the string and copy it to our In the created structure variable.

First introduce the strncpy function:

  • char * strncpy ( char * destination, const char * source, size_t num );
  • Both strncpy and strcpy copy strings in C language library functions, and strncpy adds a limit to the number of copied characters, which is more secure
  • Input three parameters, target address, source address, number of characters to copy
  • The return value is the address of the target
  • Need to include the header file <string.h>

But many parameters are int and double types, so we need to convert them from char type to double type first, and then assign values ​​to the structure members.

The explanation of stronum is explained in detail in N file reading , only the code is put here, and there are detailed comments in the code.

//将字符串转换为浮点数,i起始位置,n输入多少个字符
static double strtonum(const char* buff, int i, int n)
{
	double value = 0.0;
	char str[256] = { 0 };
	char* p = str;
	/************************************
	* 当出现以下三种情况报错,返回0.0
	* 1.起始位置<0
	* 2.读取字符串个数<i
	* 3.str里面存放的字节数<n
	*************************************/
	if (i < 0 || (int)strlen(buff) < i || (int)sizeof(str) - 1 < n)
	{
		return 0.0;
	}
	for (buff += i; *buff && --n >= 0; buff++)
	{
		//三目操作符:D和d为文件中科学计数法部分,将其转换成二进制能读懂的e
		*p++ = ((*buff == 'D' || *buff == 'd') ? 'e' : *buff);
	}
	*p = '\0';
	//三目操作符,将str中存放的数以格式化读取到value中。
	return sscanf(str, "%lf", &value) == 1 ? value : 0.0;
}

O file header read

After the preparatory work in the current period is completed, the reading work in the later period is very simple. The main ideas are as follows:

  • By each epoch as a unit, f reads in a loop
  • Each data block is read row by row using the switch statement
  • When reading, you need to check the standard O file to find the format corresponding to the data
//读取O文件数据头
extern void read_h(FILE* fp_obs, pobs_head obs_h)
{
	char buff[MAXRINEX] = { 0 };
	char* lable = buff + 60;
	int i = 0;
	int j = 0;
	while (fgets(buff, MAXRINEX, fp_obs))
	{
		if (strstr(lable, "RINEX VERSION / TYPE"))
		{
			obs_h->ver = strtonum(buff, 0, 9);
			strncpy(obs_h->type, buff + 20, 30);
			continue;
		}
		else if (strstr(lable, "APPROX POSITION XYZ"))
		{
			obs_h->apX = strtonum(buff, 0, 14);
			obs_h->apY = strtonum(buff, 0 + 14, 14);
			obs_h->apZ = strtonum(buff, 0 + 14 + 14, 14);
			continue;
		}
		else if (strstr(lable, "ANTENNA: DELTA H/E/N"))
		{
			obs_h->ANTH = strtonum(buff, 0, 14);
			obs_h->ANTdeltaE = strtonum(buff, 14, 14);
			obs_h->ANTdeltaN = strtonum(buff, 14 + 14, 14);
			continue;
		}
		else if (strstr(lable, "WAVELENGTH FACT L1/2"))
		{
			obs_h->WAFL1 = (int)strtonum(buff, 0, 6);
			obs_h->WAFL2 = (int)strtonum(buff, 6, 6);
			obs_h->WAFflag = (int)strtonum(buff, 6 + 6, 6);
			continue;
		}
		else if (strstr(lable, "# / TYPES OF OBSERV"))
		{
			obs_h->obstypenum = (int)strtonum(buff, 0, 6);
			if (obs_h->obstypenum <= 9)
			{
				for (i = 0; i < obs_h->obstypenum; i++)
				{
					strncpy(&(obs_h->obstype[i]), buff + 10 * i, 2);
				}
			}
			else if (obs_h->obstypenum > 9)
			{
				for (i = 0; i < 9; i++)
				{
					strncpy(&(obs_h->obstype[i]), buff + 10 * i, 2);
				}
				fgets(buff, MAXRINEX, fp_obs);
				for (i = 0; i < obs_h->obstypenum - 9; i++)
				{
					strncpy(&(obs_h->obstype[i + 9]), buff + 10 * i, 2);
				}
			}
			continue;
		}
		else if (strstr(lable, "INTERVAL"))
		{
			obs_h->interval = strtonum(buff, 0, 11);
			continue;
		}
		else if (strstr(lable, "TIME OF FIRST OBS"))
		{
			obs_h->f_y = (int)strtonum(buff, 0, 6);
			obs_h->f_m = (int)strtonum(buff, 6, 6);
			obs_h->f_d = (int)strtonum(buff, 6 + 6, 6);
			obs_h->f_h = (int)strtonum(buff, 6 + 6 + 6, 6);
			obs_h->f_min = (int)strtonum(buff, 6 + 6 + 6 + 6, 6);
			obs_h->f_sec = strtonum(buff, 6 + 6 + 6 + 6 + 6, 6);
			strncpy(obs_h->tsys, buff + 6 + 6 + 6 + 6 + 6 + 18, 3);
			continue;
		}
		else if (strstr(lable, "END OF HEADER"))
			break;
	}
}

In the above code, char* lable +60 is used to locate the label position of the file header. Use strstr (need to contain <string,h>) to find the matching string.

In the line of the observation value type, you need to pay attention to the same problem, that is, when there are too many observation value types, you will go to the next line. Here you need to judge whether the leftmost number is >9.

Other locations are corresponding to O files, just enter a specific format.

O file data block read

The main idea:

  • Read per epoch (big loop)
  • Every epoch, read per satellite (medium loop)
  • In each satellite, read by row (there are several rows depending on the number of observation types) (small loop)
//读取O文件数据块
extern void read_b(FILE* fp_obs, pobs_epoch obs_e, pobs_body obs_b, int type)
{
	int n = 0;//历元数
	int i = 0;//第i颗卫星
	int j = 0;//第i颗卫星的第j行观测值
	int k = 0;//第j行第k个观测值
	char buff[MAXRINEX] = { 0 };
	char flag = { 0 };//判断符号
	while (fgets(buff, MAXRINEX, fp_obs))
	{
		//按照格式将历元参考时间依次存入,年需+2000
		//除秒以外,均转换成整型
		obs_e[n].y = (int)strtonum(buff, 1, 2) + 2000;
		obs_e[n].m = (int)strtonum(buff, 4, 2);
		obs_e[n].d = (int)strtonum(buff, 7, 2);
		obs_e[n].h = (int)strtonum(buff, 10, 2);
		obs_e[n].min = (int)strtonum(buff, 13, 2);
		obs_e[n].sec = strtonum(buff, 15, 11);
		obs_e[n].p_flag = (int)strtonum(buff, 28, 1);
		//输入卫星数
		obs_e[n].sat_num = strtonum(buff, 29, 3);
		strncpy(&flag, buff + 32, 1);
		//判断卫星数是否超过12,分类处理
		if (obs_e[n].sat_num <= 12 && flag == 'G')
		{
			for (i = 0; i < obs_e[n].sat_num; i++)
			{
				//将卫星编号存储到第n个历元中,第i个PRN位置(只读数字,不读'G')
				//两个卫星编号的间隔为3个字符
				obs_e[n].sPRN[i] = strtonum(buff, 33 + 3 * i, 2);
			}
		}
		//如果卫星数超过12个,则下一行继续执行读取卫星数
		else if (obs_e[n].sat_num >= 12 && flag == 'G')
		{
			for (i = 0; i < 12; i++)
			{
				obs_e[n].sPRN[i] = strtonum(buff, 33 + 3 * i, 2);
			}
			fgets(buff, MAXRINEX, fp_obs);
			for (i = 0; i < obs_e[n].sat_num - 12; i++)
			{
				obs_e[n].sPRN[i + 12] = strtonum(buff, 33 + 3 * i, 2);
			}
		}
		//对一块历元中的i个卫星进行数据读取
		for (i = 0; i < obs_e[n].sat_num; i++)
		{
			//根据头文件读取的观测值类型判断每一颗卫星循环几行
			for (j = 0; j < (int)ceil(type / 5.0); j++)//ceil(X),返回比X大的最小整数
			{
				int count = type / 5;//计数
				fgets(buff, MAXRINEX, fp_obs);
				//依次读取一行中的观测值
				//这里加个三目操作符判断一下,是不是到了最后一行,如果到了最后一行则不进行整行读取
				for (k = 0; j == count ? k < type % 5 : k < 5; k++)
				{
					obs_b[n].obs[i][k + 5 * j] = strtonum(buff, 16 * k, 16);
				}
			}
		}
		n++;
	}
}

In the above code, you need to pay attention to: 

  • In addition to the need to pass in the file pointer and structure pointer, it is also necessary to pass in the number of observed value types read in the header file, which is convenient for judging where the data is read.
  • When reading the time, add 2000 to the year, and the second is of type double
  • The number of satellites > 12 and the number of satellites < 12 need to be considered separately
  • for (k = 0; j == count ? k < type % 5 : k < 5; k++), where j == count ? k < type % 5 : k < 5 is a ternary operator used to judge the row Whether to also read the entire line.

Different versions of the observation value file format will be different, but the general idea is the same, epoch-satellite-observation value, the above is the entire content of the observation value file read,

Guess you like

Origin blog.csdn.net/why1472587/article/details/127514022