3

Python (3.1 または 2.6) を使用して、GPS 受信機によって生成されたバイナリ データ ファイルからデータを読み取ろうとしています。1 時間ごとのデータは個別のファイルに保存され、それぞれのファイルは約 18 MiB です。データ ファイルには複数の可変長レコードがありますが、ここではそのうちの 1 つのレコードからデータを抽出する必要があります。

私は、ヘッダーをある程度デコードできるところまで持っています。一部の数字は意味をなさないので、多少言いますが、ほとんどの数字は意味があります。これに数日を費やした後 (私は 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. レコードの最初の 4 バイトを読み取って、SV 観測の数を取得します。使用structして開梱します。

    計算を行い、指定されたバイト数を読み取って、レコード内のすべての SV 観測を取得します。

    それらを開梱して、あなたがしていることは何でもしてください。

    namedtupleデータを使って他のことをする前に、データからオブジェクトを構築することを強くお勧めします。

すべてのデータが必要な場合は、実際にすべてのデータを読み取る必要があります。

「一度に 18 MiB ファイルを 1 バイトずつ読み取らずに)?」この制約がわかりません。すべてのバイトを取得するには、すべてのバイトを読み取る必要があります。

長さ情報を使用して、意味のあるチャンクでバイトを読み取ることができます。ただし、すべてのバイトを読み取ることは避けられません。

また、多くの読み取り (およびシーク) は、多くの場合、十分に高速です。OS がバッファしてくれるので、読み取り数をマイクロ最適化しようとする心配はありません。

「読み取り長 - 読み取りデータ」パターンに従ってください。

于 2010-03-02T19:32:56.547 に答える
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 に答える
2

18 MB はメモリに快適に収まるはずなので、すべてを 1 つの大きなバイト文字列にまとめてwith open(thefile, 'rb') as f: data = f.read()から、スライスですべての「解析」を実行してレコードごとに進めます。それはより便利であり、ファイルのあちこちから多くの小さな読み取りを行うよりも高速である可能性があります (ただし、以下のロジックには影響しません。どちらの場合も「データの現在の関心点」は常に移動しているからです [ [常に転送]] ヘッダーとレコードの長さを見つけるために、一度に数バイトの構造体のアンパックに基づいて計算された量によって)。

「レコードの開始」オフセットが与えられた場合、1 バイト (「フィールド 4」、ヘッダーの開始からのオフセット 3、レコードの開始と同じ) だけを見てヘッダーの長さを判断し、メッセージ ID (次のフィールド、2 バイト) を調べて、それが関心のあるレコードであるかどうかを確認します (したがって、それらの 3 バイトだけの構造体のアンパックで十分です)。

必要なレコードであるかどうかに関係なく、次にレコードの長さを計算する必要があります (スキップするか、すべて取得するかのいずれか)。そのためには、実際のレコード データの開始 (レコードの開始とヘッダーの長さとレコードの次のフィールド (ヘッダーの直後の 4 バイト) の合計) に観測の長さ (正しく読めば 32 バイト) を掛けた値を計算します。 )。

このようにして、指定する部分文字列を分離するかstruct.unpack(最終的に必要なレコードに到達したとき)、ヘッダー + レコードの合計長を「レコードの開始」オフセットに追加して、開始のオフセットを取得します。次のレコードの。

于 2010-03-02T19:31:09.750 に答える