3

使用 Python(3.1 或 2.6),我正在尝试从 GPS 接收器生成的二进制数据文件中读取数据。每小时的数据存储在一个单独的文件中,每个文件大约 18 MiB。数据文件有多个可变长度记录,但现在我只需要从其中一个记录中提取数据。

我已经能够在某种程度上解码标题。我之所以这么说,是因为有些数字没有意义,但大多数都有。在这上面花了几天(我已经开始学习使用 Python 编程)之后,我没有取得任何进展,所以是时候寻求帮助了。

参考指南给了我消息头结构和记录结构。标头可以是可变长度,但通常为 28 个字节。

Header
Field #  Field Name    Field Type    Desc                 Bytes    Offset
1        Sync          char          Hex 0xAA             1        0
2        Sync          char          Hex 0x44             1        1
3        Sync          char          Hex 0x12             1        2
4        Header Lgth   uchar         Length of header     1        3
5        Message ID    ushort        Message ID of log    2        4
8        Message Lgth  ushort        length of message    2        8
11       Time Status   enum          Quality of GPS time  1        13
12       Week          ushort        GPS week number      2        14
13       Milliseconds  GPSec         Time in ms           4        16


Record
Field #  Data                        Bytes         Format     Units       Offset
1        Header                                                           0
2        Number of SV Observations   4             integer    n/a         H
         *For first SV Observation*  
3        PRN                         4             integer    n/a         H+4
4        SV Azimuth angle            4             float      degrees     H+8
5        SV Elevation angle          4             float      degrees     H+12
6        C/N0                        8             double     db-Hz       H+16
7        Total S4                    8             double     n/a         H+24
...
27       L2 C/N0                     8             double     db-Hz       H+148
28       *For next SV Observation*
         SV Observation is satellite - there could be anywhere from 8 to 13 
         in view.

这是我试图理解标题的代码:

import struct

filename = "100301_110000.nvd"

f = open(filename, "rb")
s = f.read(28)
x, y, z, lgth, msg_id, mtype, port, mlgth, seq, idletime, timestatus, week, millis,    recstatus, reserved, version = struct.unpack("<cccBHcBHHBcHLLHH", s)

print(x, y, z, lgth, msg_id, mtype, port, mlgth, seq, idletime, timestatus, week, millis, recstatus, reserved, version)

它输出:

b'\xaa' b'D' b'\x12' 28 274 b'\x02' 32 1524 0 78 b'\xa0' 1573 126060000 10485760 3545 35358

3 个同步字段应返回 xAA x44 x12。(D 是 x44 的 ascii 等效值 - 我假设。)

我正在寻找的记录 ID 是 274 - 这似乎是正确的。

GPS 周返回为 1573 - 这似乎是正确的。

毫秒返回为 126060000 - 我期待 126015000。

如何查找标识为 274 的记录并提取它们?(我正在学习 Python 和编程,所以请记住,您给经验丰富的编码员的答案可能会让我难以理解。)

4

3 回答 3

6

你必须分段阅读。不是因为内存限制,而是因为解析要求。18MiB 很容易放入内存中。在一台 4Gb 的机器上,它在内存中的容量是 200 倍以上。

这是通常的设计模式。

  1. 仅读取前 4 个字节。用于struct仅解压缩这些字节。确认同步字节并获取标头长度。

    如果你想要标题的其余部分,你知道长度,读取其余的字节。

    如果您不想要标题,请使用seek跳过它。

  2. 读取记录的前四个字节以获取 SV Observations 的数量。用来struct解压。

    进行数学运算并读取指定的字节数以获取记录中的所有 SV 观察值。

    打开它们并做你正在做的任何事情。

    我强烈建议namedtuple先从数据中构建对象,然后再对它进行任何其他操作。

如果您想要所有数据,则必须实际读取所有数据。

“并且没有一次读取一个字节的 18 MiB 文件)?” 我不明白这个约束。您必须读取所有字节才能获取所有字节。

您可以使用长度信息来读取有意义的块中的字节。但是你不能避免读取所有字节。

此外,大量读取(和查找)通常足够快。您的操作系统会为您缓冲,因此不必担心尝试微优化读取次数。

只需遵循“读取长度-读取数据”模式即可。

于 2010-03-02T19:32:56.547 回答
2

18 MB 应该很适合内存,所以我只需将整个内容吞入一个大字节字符串中with open(thefile, 'rb') as f: data = f.read(),然后在切片上执行所有“解析”以逐个记录地推进记录。它更方便,并且可能比从文件中的各处进行许多小读取要快(尽管它不影响下面的逻辑,因为在任何一种情况下,“数据中的当前兴趣点”总是在移动 [ [总是向前,因为它发生]] 根据一次几个字节的结构解包计算的数量,以查找标题和记录的长度)。

给定“记录开始”偏移量,您可以通过仅查看一个字节(“字段四”,与记录开始相同的标题开始的偏移量 3)并查看消息 ID(下一个字段)来确定其标题的长度, 2 个字节)来查看它是否是您关心的记录(因此仅这 3 个字节的结构解包就足够了)。

不管是不是你想要的记录,接下来你需要计算记录的长度(要么跳过它,要么全部获取);为此,您计算实际记录数据的开始(记录的开始加上标题的长度加上记录的下一个字段(标题后面的 4 个字节)乘以观察的长度(如果我没看错,则为 32 个字节) )。

这样你要么隔离要给予的子字符串struct.unpack(当你最终到达你想要的记录时),或者只是将标题+记录的总长度添加到“记录开始”偏移量,以获得开始的偏移量的下一个记录。

于 2010-03-02T19:31:09.750 回答
2

除了编写一个正确读取文件的解析器之外,您还可以尝试一种有点暴力的方法......将数据读取到内存并使用“同步”哨兵将其拆分。警告 - 你可能会得到一些误报。但...

f = open('filename')
data = f.read()
messages = data.split('\xaa\x44\x12')
mymessages = [ msg for msg in messages if len(msg) > 5 and msg[4:5] == '\x12\x01' ]

但这是一个非常讨厌的黑客......

于 2010-03-02T19:38:00.387 回答