2020年7月6日 23:17:31
环境:VS2013 C语言
最近在处理一些较大的数据,今天想要将一组超过百G的信号采集数据进行信号处理后,在重新存储起来,作为后续程序的起始数据。
在看了网上诸多的什么比如用类人算的方法将整数拆解为2进制、直接存储整数格式等建议之后,我感觉这些都不是我想要的。
受到读取数据16bit有符号数据的启发:(如下,小端模式)
我也想将存储的数据也得到类似于此的性能,即存储数据的大小SizeF严格遵循
SizeF = 2(Byte)* Fs * t
其中Fs为采样率,t为采样时间。
但是使用整形数据直接储存或16进制直接储存并不能满足。
最后我将本问题的解决方案定为:也就是想要读取一组格式为小端模式的16bit有符号数据,并将其进行信号处理后,重新以最有效的16bit有符号数据格式存储(大端模式)起来。。最后也实现了。
一开始的测试,用了一个大小(393750kB)的原数据做例子:
以上为测试的结果,从上到下分别以5位整数(%5d)、4位16进制(%04x)以及16位2进制的不同方式存储。
可以很明显的看出存储方式对空间利用的优化。
其实实现的代码也很简单:
对小端模式的16bit读取重组(作者电脑C以大端模式读取数据),代码如下:
int tek_read(int i_offset, long long int offset, int readSum) { FILE *odata = NULL; char *obuf = NULL; size_t bytes_read; //int16_t mid_data[DATA_BUF_SIZE]; int16_t *mid_data; mid_data = (int16_t *)calloc((2 * readSum), sizeof(int16_t)); int16_t temp = 0x00ff; int16_t temp1 = 0x0000; //用来屏蔽污染中间变量,详情见下 int i, j, l; int d, length = 0; int flag = 10; if (mid_data == NULL) { return FAILURE; } odata = fopen(DATA_name, "r+"); if (odata == NULL) { free(mid_data); return FAILURE; } obuf = (char *)malloc((readSum * 2) * sizeof(char)); //动态分配内存 储存连续readSum *16b 数据 if (obuf == NULL) { free(mid_data); free(obuf); return FAILURE; } long long int t_offset; t_offset = i_offset * offset * 2; flag = _fseeki64(odata, t_offset, 0); bytes_read = fread(obuf, 1, (2 * readSum), odata); fclose(odata); if (bytes_read < (2 * readSum + 1)) { for (i = 0; i < (2 * readSum); i++) { mid_data[i] = obuf[i]; } memset(Iri_data, 0, (2 * readSum)); //将Iri_data元素换成0 for (j = 0; j < readSum; j++) { Iri_data[j] = mid_data[2 * j + 1]; Iri_data[j] <<= 8; temp1 = mid_data[2 * j] & temp; //某些低8位(类型是16位)的前8位读成ff导致或运算之后最终前8位成为ff, //为避免污染,先与0x00ff与运算 Iri_data[j] |= temp1; } free(obuf); //读到Iri_data中后就释放内存 //数组长度 d = 0; l = 0; //可能有连续5个0????末尾是0导致的问题 free(mid_data); //释放中间内部 return length; } else { printf("* I am sorry to tell u there is a trouble in taking Tektronix DATA! Please cheak your settings! "); free(mid_data); //释放中间内存 return 0; } }
中间的信号处理就不详细介绍了
最后的大端模式将16bit有符号数据写入文件,实现代码:
int outfile(void) { FILE *fp_out; fp_out = fopen(outname, "a+"); for (int i = 0; i < length_90ms_7Mhz; i++) { int16_t temp = Iridium_result_D16[i]; //大端模式 fputc((temp >> 8) & 0xFF, fp_out); fputc(temp & 0xFF, fp_out); } fclose(fp_out); return SUCCESS; }
对于我200个G的原始数据,预处理效果很理想。
存储数据效果如下:
2020年7月7日18:13:34 sun 修改
在使用整个程序发现了一些问题并相应的进行了一些修改,记录如下。
首先,由写入数据部分的启发,将读取原数据部分(即读取小端模式,16bit有符号数据)修改为:
int data_read(int i_offset, long long int offset, int readSum) { FILE *odata = NULL; int16_t mid_data = 0; int16_t temp = 0x0000; //用来作为中间变量,详情见下 int length = 0; int flag = 10; odata = fopen(DATA_name, "r+"); if (odata == NULL) { return FAILURE; } long long int t_offset; t_offset = i_offset * offset * 2; flag = _fseeki64(odata, t_offset, 0); memset(Iri_data, 0, readSum); //将Iri_data元素换成0 for (int i = 0; i < readSum; i++) { char obuf0 = fgetc(odata); char obuf1 = fgetc(odata); Iri_data[i] = obuf1; Iri_data[i] << 8; Iri_data[i] &= 0xff00; temp = obuf0 & 0x00ff; Iri_data[i] |= temp; length = i + 1; } fclose(odata); return length; }
在以上的写入数据程序,我发现写入数据的文件大小总是和理论上的不同,数据的实际大小总是略大于计算的大小,而用查看文件时其数目也与我写入的数据不符,查了一圈资料,发现了问题,要用2进制(wb)方式写入:
int data_outfile(int i_offset, long long int offset, int writeSum) { FILE *fp_out; fp_out = fopen(outname, "ab+"); int flag = 10; for (int i = 0; i < writeSum; i++) { int16_t temp = Iridium_result_D16[i]; //大端模式 fputc((temp >> 8) & 0xFF, fp_out); fputc(temp & 0xFF, fp_out); } fclose(fp_out); return SUCCESS; }
ab+是在文件末尾以2进制格式写入,
类似于上上部分,读取自己写入的数据的程序如下,注意(rb+):
int data_read(int i_offset, long long int offset, int readSum) { FILE *odata = NULL; size_t bytes_read; int16_t mid_data = 0; int16_t temp = 0x0000; //中间变量,详情见下 int length = 0; int flag = 10; odata = fopen(DATA_name, "rb+"); if (odata == NULL) { return FAILURE; } long long int t_offset; t_offset = i_offset * offset * 2; flag = _fseeki64(odata, t_offset, 0); //bytes_read = fread(obuf, 1, (2 * readSum), odata); memset(Iri_data, 0, readSum); //将Iri_data元素换成0 //使用fgetc,不需要obuf使用 for (int i = 0; i < readSum; i++) { char obuf0 = fgetc(odata); char obuf1 = fgetc(odata); mid_data = obuf0; mid_data << 8; mid_data &= 0xff00; temp = obuf1 & 0x00ff; mid_data |= temp; Iri_data[i] = mid_data; length = i + 1; //读取长度 } fclose(odata); return length; }