用python读取dat文件,wifi数据采集工具csi tool数据文件(.dat文件)解析
项目地址:
https://github.com/hongshixian/CSI_reader
前言
数据采集工具csi_tool采集数据并保存为后缀.dat的数据文件,在csi_tool中提供一个c语言函数解析此文件。阅读了c语言的解析代码后发现,数据文件的组织方法与计网中数据十分相似,但略有不同。
参考代码
一部分c语言代码:
unsigned int timestamp_low = inBytes[0] + (inBytes[1] << 8) + (inBytes[2] << 16) + (inBytes[3] << 24);
unsigned short bfee_count = inBytes[4] + (inBytes[5] << 8);
unsigned int Nrx = inBytes[8];
unsigned int Ntx = inBytes[9];
unsigned int rssi_a = inBytes[10];
unsigned int rssi_b = inBytes[11];
unsigned int rssi_c = inBytes[12];
char noise = inBytes[13];
unsigned int agc = inBytes[14];
unsigned int antenna_sel = inBytes[15];
unsigned int len = inBytes[16] + (inBytes[17] << 8);
unsigned int fake_rate_n_flags = inBytes[18] + (inBytes[19] << 8);
数据格式
总体上,整个文件仅由n个bfee组成,巧了,数据文件中应当包含有n个采样信息,这个bfee的意义不言而喻,就是和采样一一对应。
bfee:
bfee的数据结构如上图所示。
前两字节是field_len,之后一字节是code,再之后便是可变长度的field。field_len等于code+field的字长。
当code为187时,表示field中是信道信息;不是187时,表示field中是其他信息。
我们关心的是信道信息,其他信息不解析,跳过该bfee即可。
field:
若code等于187,field有如上图数据格式。
到这里你一定感觉很熟悉了。
field分为头部和有效载荷(payload)两部分。头部有20字节的固定长度,有效载荷是个可变长度,字长为len。
头部各字段的数据类型和意义如下表:
可以见得,头部中包含了主要的信道信息。
而其中最重要的csi矩阵,分为30个subc,保存在有效载荷中。
分别对应30个子载波。
subc的结构如下表所示:
复数的结构:
每个subc的开始会有3位的非数据部分,因此subc的长度不是字节(8位)的整数倍,这将导致subc这部分的解析需要按比特操作,增加我解析工作的复杂度。
到这里,整个文件的数据结构都清楚了,开始试着用python来解析run-lxx.dat这个文件。
(真想交给王福超来写啊zzz)
文件解析
我依旧会使用面向对象的方式构建类,不过构造方法无力,属性太多,我选择用静态方法添加属性的方式构建对象。
import numpy as np
class Bfee: def __init__(self): pass @staticmethod def from_file(filename, model_name_encode="shift-JIS"): with open(filename, "rb") as f: from functools import reduce array = bytes(reduce(lambda x, y: x+y, list(f))) # reduce(函数,list),将list中元素依次累加 bfee = Bfee() # vmd.current_index = 0 bfee.file_len = len(array) bfee.dicts = [] bfee.all_csi = [] # vmd.timestamp_low0 = int.from_bytes(array[3:7], byteorder='little', signed=False) # array = array[3:] #%% Initialize variables #ret = cell(ceil(len/95),1); # % Holds the return values - 1x1 CSI is 95 bytes big, so this should be upper bound cur = 0 # % Current offset into file count = 0 # % Number of records output broken_perm = 0 # % Flag marking whether we've encountered a broken CSI yet triangle = [0, 1, 3] # % What perm should sum to for 1,2,3 antennas while cur < (bfee.file_len - 3): #% Read size and code #% 将文件数据读取到维度为 sizeA 的数组 A 中,并将文件指针定位到最后读取的值之后。fread 按列顺序填充 A。 bfee.field_len = int.from_bytes(array[cur:cur+2], byteorder='big', signed=False) bfee.code = array[cur+2] cur = cur+3 # there is CSI in field if code == 187,If unhandled code skip (seek over) the record and continue if bfee.code == 187: pass else: #% skip all other info cur = cur + bfee.field_len - 1 continue # get beamforming or phy data if bfee.code == 187: count =count + 1 bfee.timestamp_low = int.from_bytes(array[cur:cur+4], byteorder='little', signed=False) bfee.bfee_count = int.from_bytes(array[cur+4:cur+6], byteorder='little', signed=False) bfee.Nrx = array[cur+8] bfee.Ntx = array[cur+9] bfee.rssi_a = array[cur+10] bfee.rssi_b = array[cur+11] bfee.rssi_c = array[cur+12] bfee.noise = array[cur+13] - 256 bfee.agc = array[cur+14] bfee.antenna_sel = array[cur+15] bfee.len = int.from_bytes(array[cur+16:cur+18], byteorder='little', signed=False) bfee.fake_rate_n_flags = int.from_bytes(array[cur+18:cur+20], byteorder='little', signed=False) bfee.calc_len = (30 * (bfee.Nrx * bfee.Ntx * 8 * 2 + 3) + 6) / 8 bfee.csi = np.zeros(shape=(30,bfee.Nrx ,bfee.Ntx), dtype=np.dtype(np.complex)) bfee.perm = [1,2,3] bfee.perm[0] = ((bfee.antenna_sel) & 0x3) bfee.perm[1] = ((bfee.antenna_sel >> 2) & 0x3) bfee.perm[2] = ((bfee.antenna_sel >> 4) & 0x3) cur = cur + 20 #get payload payload = array[cur:cur+bfee.len] cur = cur + bfee.len index = 0 #Check that length matches what it should if (bfee.len != bfee.calc_len): print("MIMOToolbox:read_bfee_new:size","Wrong beamforming matrix size.") #Compute CSI from all this crap : #import struct for i in range(30): index += 3 remainder = index % 8 for j in range(bfee.Nrx): for k in range(bfee.Ntx): real_bin = bytes([(payload[int(index / 8)] >> remainder) | (payload[int(index/8+1)] << (8-remainder)) & 0b11111111]) real = int.from_bytes(real_bin, byteorder='little', signed=True) imag_bin = bytes([(payload[int(index / 8+1)] >> remainder) | (payload[int(index/8+2)] << (8-remainder)) & 0b11111111]) imag = int.from_bytes(imag_bin, byteorder='little', signed=True) tmp = np.complex(float(real), float(imag)) bfee.csi[i, j, k] = tmp index += 16 # % matrix does not contain default values if sum(bfee.perm) != triangle[bfee.Nrx-1]: print('WARN ONCE: Found CSI (',filename,') with Nrx=', bfee.Nrx,' and invalid perm=[',bfee.perm,']\n' ) else: temp_csi = np.zeros(bfee.csi.shape, dtype=np.dtype(np.complex)) # bfee.csi[:,bfee.perm[0:bfee.Nrx],:] = bfee.csi[:,0:bfee.Nrx,:] for r in range(bfee.Nrx): temp_csi[:,bfee.perm[r],:] = bfee.csi[:,r,:] bfee.csi = temp_csi # print phy data # print(vmd.file_len, # vmd.field_len, # vmd.code, # vmd.timestamp_low, # vmd.bfee_count, # vmd.Nrx, # vmd.Ntx, # vmd.rssi_a, # vmd.rssi_b, # vmd.rssi_c, # vmd.noise, # vmd.agc, # vmd.antenna_sel, # vmd.len, # vmd.fake_rate_n_flags, # vmd.calc_len, # vmd.perm, # vmd.csi.shape # ) # 将类属性导出为dict,并返回 bfee_dict = {} bfee_dict['timestamp_low'] = bfee.timestamp_low bfee_dict['bfee_count'] = bfee.bfee_count bfee_dict['Nrx'] = bfee.Nrx bfee_dict['Ntx'] = bfee.Ntx bfee_dict['rssi_a'] = bfee.rssi_a bfee_dict['rssi_b'] = bfee.rssi_b bfee_dict['rssi_c'] = bfee.rssi_c bfee_dict['noise'] = bfee.noise bfee_dict['agc'] = bfee.agc bfee_dict['antenna_sel'] = bfee.antenna_sel bfee_dict['perm'] = bfee.perm bfee_dict['len'] = bfee.len bfee_dict['fake_rate_n_flags'] = bfee.fake_rate_n_flags bfee_dict['calc_len'] = bfee.calc_len bfee_dict['csi'] = bfee.csi bfee.dicts.append(bfee_dict) bfee.all_csi.append(bfee.csi) return bfee
if __name__ == '__main__': bfee = Bfee.from_file("run-lxx.dat", model_name_encode="gb2312") from pprint import pprint pprint(len(bfee.dicts)) pprint(len(bfee.all_csi))
4993 4993
其他和总结
方法的返回两种结果:
bfee.dicts字段等同于read_bfee_file() 函数的返回的结果,适用于原来的处理步骤。
bfee.all_csi字段是所有csi矩阵的列表,可以直接转化成numpy数组,用来弥补字典性能低下的问题。
两个长度一样。
temp = np.array(vmd.all_csi) np.savez('run-lxx.npz', temp) temp.shape
(4993, 30, 3, 2)
保存为npz格式,
run-lxx.dat大小1.9Mb,run-lxx.npz变成了14.4Mb
两种文件的数据是一样多的,dat文件中复数的实部虚部用8位的sign int表示,npz文件中用64位的double表示,数据长度是原来的8倍,文件大小也变8倍。
可见.dat文件占用比较小
正确的matlab解析步骤应该是:
1.从文件将头部信息和csi矩阵读取到字典,即read_bfee_file()
2.依次从字典中取出标准化CSI,即get_scale_csi()
3.将所有csi整合到一起,保存为csv
标准化get_scale_csi()这个函数并不复杂,python实现之后,
python便可读取并处理dat文件。
标准化get_scale_csi()
import numpy as npimport math
def db(X, U): R = 1 if 'power'.startswith(U): assert X >= 0 else: X = math.pow(abs(X), 2) / R return (10 * math.log10(X) + 300) - 300
def dbinv(x): return math.pow(10, x / 10)
def get_total_rss(csi_st): # Careful here: rssis could be zero rssi_mag = 0 if csi_st['rssi_a'] != 0: rssi_mag = rssi_mag + dbinv(csi_st['rssi_a']) if csi_st['rssi_b'] != 0: rssi_mag = rssi_mag + dbinv(csi_st['rssi_b']) if csi_st['rssi_c'] != 0: rssi_mag = rssi_mag + dbinv(csi_st['rssi_c']) return db(rssi_mag, 'power') - 44 - csi_st['agc']
def get_scale_csi(csi_st): #Pull out csi csi = csi_st['csi'] # print(csi.shape) # print(csi) #Calculate the scale factor between normalized CSI and RSSI (mW) csi_sq = np.multiply(csi, np.conj(csi)).real csi_pwr = np.sum(csi_sq, axis=0) csi_pwr = csi_pwr.reshape(1, csi_pwr.shape[0], -1) rssi_pwr = dbinv(get_total_rss(csi_st)) scale = rssi_pwr / (csi_pwr / 30) if csi_st['noise'] == -127: noise_db = -92 else: noise_db = csi_st['noise'] thermal_noise_pwr = dbinv(noise_db) quant_error_pwr = scale * (csi_st['Nrx'] * csi_st['Ntx']) total_noise_pwr = thermal_noise_pwr + quant_error_pwr ret = csi * np.sqrt(scale / total_noise_pwr) if csi_st['Ntx'] == 2: ret = ret * math.sqrt(2) elif csi_st['Ntx'] == 3: ret = ret * math.sqrt(dbinv(4.5)) return ret
get_scale_csi()是文件解析中常用的函数,随csi tool一起以matlab代码提供给使用者
在python中使用这个函数,要感谢 csi交流群3 中Siney同学的帮助,此函数的python代码这部分是由ta完成的
你可以根据需要,灵活的使用get_scale_csi()在你的python脚本中
类似这样:
if __name__ == '__main__': bfee = Bfee.from_file("csi.dat", model_name_encode="gb2312") for i in range(len(bfee.all_csi)): csi = get_scale_csi(bfee.dicts[i]) print(csi[:,:,i])
[[-4.61880108-10.39230244j 14.43375551 +5.19615198j 5.1961519 +1.73205063j] [12.12435284+23.67135555j 2.30940088 +8.08290308j -5.77350211+17.32050633j]] [[ -9.23760101+10.96965119j -14.43375542 -0.57735022j 0. +4.61880157j] [ 19.62990214-23.67135258j -5.19615195 -7.50555282j -19.62990667 +1.15470039j]] [[-11.54700066 +8.66025049j -6.92820252+12.70170462j -2.30940073 -1.15470037j] [ 29.44485168-15.58845089j -10.96965399 +0.57735021j 8.08290256-20.20725641j]] [[ 4.61880029+13.85640088j 8.66025261+15.5884547j 0. +1.73205055j] [ 1.15470007-32.90895209j -8.08290244+12.12435366j -17.89785573+12.12435388j]] [[ 16.16579928 +4.61879979j -16.74315425 -4.61880117j 1.15470031 +0.57735015j] [-24.82604889-20.2072491j -4.04145103-15.01110381j 22.51665597 +2.30940061j]] [[-12.12435077-13.27905085j 4.61880156+12.7017043j -2.8867509 -1.15470036j] [ 1.15470007+31.17690199j -9.81495332+11.54700391j -18.47520576+12.12435378j]] [[ 15.01109927+13.27904936j 5.77350143+12.70170315j 4.04145118 +2.88675084j] [ -6.92819966-29.44484858j -10.96965272+15.58845387j 16.16580471-12.7017037j ]] [[-13.85640139+16.74315168j 9.81495338 -1.7320506j 5.19615164 +5.77350182j] [ 26.55810266 -1.15470012j 12.12435417+12.12435417j 18.47520582 -9.23760291j]] [[-24.82604699+12.70169846j -3.46410083+10.39230249j 9.81495259 -1.73205046j] [ 28.8674965 +8.66024895j -21.93930526 +6.92820166j -0.57735015-21.93930579j]] [[ -8.08289687-23.09399106j 4.61880085 -6.35085117j -10.39230116 +0.j ] [-12.12434531+21.93929151j 18.4752034 -1.73205032j -4.04145045+19.6299022j ]]
文中所有代码及数据文件可以在这里获取:
https://github.com/hongshixian/CSI_reader
- 点赞
- 收藏
- 分享
- 文章举报
- python读取各种文件数据方法解析
- C#创建DAT文件和读取DAT文件数据
- python从oracle读取数据写入到文件里
- Python数据分析之读取文件
- python从共享目录读取考勤数据,通过openpyxl解析excel2007,查询出自己考勤记录
- Python 打开文件读取每行数据
- 使用python pandas读取csv文件数据
- SparkStreaming python 读取kafka数据将结果输出到单个指定本地文件
- python 数据文件的读取
- Python3实现将文件归档到zip文件及从zip文件中读取数据的方法
- cocos2dx 解析数据之读取JSON文件
- IIS7.5支持解析读取.json文件数据
- java解析properties 从a文件读取数据到b文件里
- python的读取csv文件数据
- python使用pandas读取数据文件
- Python读取本地文件并解析网页元素
- 002_009 Python 从Zip中读取数据 直接检查一个zip格式的归档文件部分或所有文件而且不用解压
- cocos2dx 2.2.2 cocostudio 数据编辑器导出的.json文件读取 解析
- Python读取文件数据
- Jsoup学习笔记9:Jsoup 解析saz文件,读取其中的htm文件到字符串,提取字符串中的数据写入csv文件中