Python 将二进制时间戳转换为字符串

Python 将二进制时间戳转换为字符串,python,c,binary,endianness,bitstring,Python,C,Binary,Endianness,Bitstring,我正试图用python解析一种专有的二进制格式(Wintec NAL)。现有的和正在运行的C代码也是这样做的(作者:Dennis Heynlein),我正在尝试将其移植到Python 我正在努力理解C代码的部分内容。以下是C中二进制格式的定义: /* * File extension:. NAL * File format: binary, 32 byte fixed block length */ /* * For now we will read raw structs direc

我正试图用python解析一种专有的二进制格式(Wintec NAL)。现有的和正在运行的C代码也是这样做的(作者:Dennis Heynlein),我正在尝试将其移植到Python

我正在努力理解C代码的部分内容。以下是C中二进制格式的定义:

/*
 * File extension:. NAL
 * File format: binary, 32 byte fixed block length
 */

/*
 * For now we will read raw structs direct from the data file, ignoring byte
 * order issues (since the data is in little-endian form compatible with i386)
 *
 * XXX TODO:  write marshalling functions to read records in the proper
 * byte-order agnostic way.
 */
#pragma pack (1)

typedef struct nal_data32 {
  unsigned char point_type; /* 0 - normal, 1 - start, 2 - marked */

  unsigned char padding_1;

  unsigned int second: 6, minute: 6, hour: 5;
  unsigned int day: 5, month: 4, year: 6; /* add 2000 to year */

  signed int latitude;    /* divide by 1E7 for degrees */
  signed int longitude;   /* divide by 1E7 for degrees */

  unsigned short height;    /* meters */

  signed char temperature;  /* °C */

  unsigned short pressure;  /* mbar */

  unsigned char cadence;    /* RPM */
  unsigned char pulse;    /* BPM */

  signed char slope;    /* degrees */

  signed short compass;   /* °Z axis */
  signed short roll;    /* °X axis */
  signed short yaw;   /* °Y axis */

  unsigned char speed;    /* km/h */

  unsigned char bike;   /* ID# 0-3 */

  unsigned char padding_2;
  unsigned char padding_3;
} nal_t;
我使用python位字符串在python中复制此功能,但是我很难理解上面给出的时间格式并将其应用到python中

from bitstring import ConstBitStream
nal_format=('''
    uint:8,
    uint:8,
    bin:32,
    intle:32,
    intle:32,
    uint:16,
    uint:8,
    uint:16,
    uint:8,
    uint:8,
    uint:8,
    uint:16,
    uint:16,
    uint:16,
    uint:8,
    uint:8,
    uint:8,
    uint:8
''')

f = ConstBitStream('0x01009f5a06379ae1cb13f7a6b62bca010dc703000000c300fefff9ff00000000')
f.pos=0

#type,padding1,second,minute,hour,day,month,year,lat,lon,height,temp,press,cad,pulse,slope,compass,roll,yaw,speed,bike,padding2,padding3=f.peeklist(nal_format)

type,padding1,time,lat,lon,height,temp,press,cad,pulse,slope,compass,roll,yaw,speed,bike,padding2,padding3=f.readlist(nal_format)

print type
print padding1
#print second 
#print minute
#print hour
#print day
#print month
#print year
print time
print lat
print lon
虽然我已经计算出纬度和经度必须定义为little endian,但我不知道如何调整32位宽的时间戳,使其符合C定义中给出的格式(我也无法计算出与“高度”匹配的掩码-相应地,我没有尝试后面的字段)

以下是上述十六进制字符串的值:

  • 日期:2013/12/03-T05:42:31
  • 位置:73.3390583°E,33.2128666°N
  • 罗盘:195°,横滚-2°,偏航-7°
  • 高度:458米
  • 温度:13摄氏度
  • 压力:967MB

C结构中的时间戳是一个“C”位字段。编译器使用冒号后的数字在较大的字段定义中分配若干位。在本例中,为无符号整数(4字节)。寻找更好的解释。对于位字段,最大的问题是位是根据计算机的endian类型分配的,因此它们不是很可移植的

Python格式声明中似乎有错误。它可能应该为日期分配额外的4字节无符号int。比如:

nal_format=('''
    uint:8,
    uint:8,
    bin:32,
    bin:32,
    intle:32,
    intle:32,
''')
要在Python中表示位字段,请使用Python位数组来表示位。退房


另一件需要注意的事情是,结构上的组件(1)。它告诉编译器在一个字节边界上对齐。换句话说,不要在字段之间添加任何填充。通常,对齐为4字节,导致编译器在4字节边界上启动每个字段。查看更多信息。

我不熟悉
位字符串
,因此我将把您的输入转换为压缩二进制数据,然后使用
struct
来处理它。如果你对那部分不感兴趣,就跳到休息处

import binascii

packed = binascii.unhexlify('01009f5a06379ae1cb13f7a6b62bca010dc703000000c300fefff9ff00000000')
如果你愿意,我可以更详细地看一下这部分。它只是把
'0100…'
变成
b'\x01\x00…'

现在,解包中唯一的“问题”是,您只想解包一个无符号int,因为该位字段适合32位(单个无符号int的宽度):

在函数形式上,整个过程更为自然:

def unmask(num, width, offset):
     return (num & (2**width - 1) << offset) >> offset
如果你想变得花哨

from itertools import starmap
from functools import partial

width_offsets = [(6,0),(6,6),(5,12),(5,17),(4,22),(6,26)]

list(starmap(partial(unmask,time), width_offsets))
Out[166]: [31, 42, 5, 3, 12, 13L]
正确设置所有这些数字的格式,最终得出预期的日期/时间:

'20{:02d}/{:02d}/{:02d}-T{:02d}:{:02d}:{:02d}'.format(*reversed(_))
Out[167]: '2013/12/03-T05:42:31'

(可能有一种方法可以用
位字符串
模块优雅地完成所有这些位数学运算,但我发现从第一原理解决问题是令人满意的。)

不,不需要额外的无符号int,他的格式规范是正确的。位字段中的所有位加起来是32位,因此可以放入一个无符号整数中。感谢您的指针和链接!我的主要问题是时间戳位域所需的二进制计算,@roippi在他的回答中详细解释了这一点。谢谢@roippi的更正。关于位的位置,你是对的。我知道位字段,这就是为什么我从struct切换到bitstring,因为我认为它可以使我拆分位字段的时间元素。但最终我还是不知道如何处理位字段,因为简单地分割位是行不通的。多亏了你对所涉及的数学问题的解释,我才使它工作起来——我自己永远也无法解决这个问题:——)
def unmask(num, width, offset):
     return (num >> offset) & (2**width - 1)

unmask(time, 6, 0)
Out[77]: 31

unmask(time, 6, 6)
Out[78]: 42

#etc
from itertools import starmap
from functools import partial

width_offsets = [(6,0),(6,6),(5,12),(5,17),(4,22),(6,26)]

list(starmap(partial(unmask,time), width_offsets))
Out[166]: [31, 42, 5, 3, 12, 13L]
'20{:02d}/{:02d}/{:02d}-T{:02d}:{:02d}:{:02d}'.format(*reversed(_))
Out[167]: '2013/12/03-T05:42:31'