3

socket.recv()を使用して、ソケット(bluetooth)を介してデバイスからバイナリデータを受信して​​います。

デコードして処理するのに十分なデータが受信されるまで、リストまたはバイト配列でのバッファリングを検討しました。つまり、開始フラグと停止フラグ(バイト)を受け取るまでです。

ただし、デバイスは「オクテットスタッフィング」を適用しています。つまり、次の2バイトが出現するたびに、次のように1バイトに置き換える必要があります。

dic = { '\xFE\xDC' : '\xFC' , '\xFE\xDD' : '\xFD' , '\xFE\xDE' : '\xFE'}

また、データを送信する場合は、その逆を適用する必要があります。たとえば、1バイトの0xFC->は、2バイトの0xFE、0xDCを生成します。

実際に発生するのは、スタッフィング(データの送信)時に、0xFC、0xFD、0xFEのいずれかが検出された場合、バイトの前に0xFEが追加され、それ自体が0x20のXORになります。スタッフィング解除(データの受信)時に、0XFEは破棄され、次のバイトはXORされて0x20になります。

私がPythonにかなり慣れていないと言うのは、控えめな表現です。私は昨日コーディングを開始し、インターフェースを稼働させました。ただし、これには注意が必要です。

データを文字列に入れて置換できることはわかっています。しかし、バイナリデータを文字列にパックするには、置換してからアンパックしてデコードするのは少し非効率的です。

また、着信データを監視し、0xFEフラグが表示されたときに行動することもできます。いいのは、リストやバイト配列などを詰め込んだり、詰め込んだりする方法です。

リストまたはバイト配列内の単一バイトを置き換えることはそれほど難しいことではないように思われますが、1つを2つに置き換えるか、またはその逆です...?

どんな助けでも大歓迎です。

(ちなみにこれはPython 2.7です。)

4

1 に答える 1

6

概要

バイトストリームをラップし、特定の値をエスケープする必要があります。また、逆の方法が必要です。制御コードをエスケープ解除して、生のペイロードを取得します。ソケットを使用しています。socket-commandsはstring-parametersを使用します。Pythonでは、すべての文字列は基本的にchar*-arrayのラッパーです。

素朴なアプローチ

その文字列であり、特定の値を他の値に置き換えたいと考えています。では、これを達成するための最も簡単な方法は何ですか?

def unstuff(self, s):
    return s.replace('\xFE\xDC', '\xFC').replace('\xFE\xDD', '\xFE').replace('\xFE\xDE', '\xFE')
def stuff(self, s):
    return s.replace('\xFC', '\xFE\xDC').replace('\xFD', '\xFE\xDD').replace('\xFE', '\xFE\xDE')

悪いようです。replace-callごとに、新しい文字列コピーが作成されます。

イテレータ

非常にPython的なアプローチは、この特定の問題のイテレータを定義することです。入力データを目的の出力に変換するイテレータを定義します。

def unstuff(data):
    i = iter(data)
    dic = {'\xDC' : '\xFC', '\xDD' : '\xFD', '\xFE' : '\xDE'}
    while True:
        d = i.next()  # throws StopIteration on the end
        if d == '\xFE':
            d2 = i.next()
            if d2 in dic:
                yield dic[d2]
            else:
                yield '\xFE'
                yield d2
        else:
            yield d

def stuff(data):
    i = iter(data)
    dic = { '\xFC' : '\xDC', '\xFD' : '\xDD', '\xFE' : '\xDE' }
    while True: 
        d = i.next() # throws StopIteration on the end
        if d in dic:
            yield '\xFE'
            yield dic[d]
        else:
            yield d

def main():
    s = 'hello\xFE\xDCWorld'
    unstuffed = "".join(unstuff(s))
    stuffed = "".join(stuff(unstuffed))
    print s, unstuffed, stuffed

    # also possible
    for c in unstuff(s):
        print ord(c)

if __name__ == '__main__':
    main()

stuff()unstuff()反復可能なもの(リスト、文字列、...)が必要で、iterator-objectを返しますprint結果を取得したり、に渡したりする場合はsocket.send、(で示されているように)文字列に戻す必要があります"".join()。予期しないデータはすべて何らかの方法で処理0xFE 0x__されます。どのパターンとも一致しない場合は、逐語的に返されます。

RegExp

別の方法は、正規表現を使用することです。それは大きなトピックであり、時には問題の原因にもなりますが、単純に保つことができます。

import re

s = 'hello\xFE\xDCWorld' # our test-string

# read: FE DC or FE DD or FE DE
unstuff   = re.compile('\xFE\xDC|\xFE\xDD|\xFE\xDE')
# read: 
#  - use this pattern to match against the string
#  - replace what you have found (m.groups(0), whole match) with
#    char(ord(match[1])^0x20)
unstuffed = unstuff.sub(lambda m: chr(ord(m.group(0)[1])^0x20), s)

# same thing, other way around
stuff   = re.compile('\xFC|\xFD|\xFE')
stuffed = stuff.sub(lambda m: '\xFE' + chr(ord(m.group(0))^0x20), unstuffed)

print s, unstuffed, stuffed

前述のように、ソケットで使用できるようにするには、どこかに新しい文字列を作成する必要があります。少なくとも、このアプローチでは、文字列の不要なコピーが作成されることはありませんs.replace(..).replace(..).replace(..)。これらのオブジェクトの構築は比較的費用がかかるため、パターンstuffunstuffその周辺を維持する必要があります。

ネイティブC関数

Pythonで速度が低下する場合は、cpythonを使用してCコードとして実装することをお勧めします。基本的に、私は最初の実行を行い、オタクのバイト数を数え、新しい文字列を割り当て、2回目の実行を行います。私はpython-c-extensionsにあまり慣れていないので、このコードを共有したくありません。うまくいくようです。次の章を参照してください

比較

最適化の最も重要なルールの1つ:比較してください!すべてのテストの基本設定:

generate random binary data as a string
while less_than_a_second:
    unstuff(stuff(random_data))
    count += 1
return time_needed / count

私は知っている、セットアップは最適ではありません。しかし、いくつかの有用な結果が得られるはずです。

4つの方法の比較

何が見えますか?ネイティブが最速の方法ですが、非常に小さい文字列の場合のみです。これはおそらくpython-interpreterが原因です。必要な関数呼び出しは3つではなく1つだけです。しかし、ほとんどの場合、マイクロ秒は十分に高速です。〜500バイト後、タイミングは単純なアプローチとほぼ同じです。実装では、そこにいくつかの深い魔法が起こっているに違いありません。イテレータと正規表現は、努力と比較して受け入れられません。

要約すると、単純なアプローチを使用します。何かを良くするのは難しい。また、単にタイミングを推測すると、ほとんどの場合間違っています。

于 2012-11-23T14:09:07.143 に答える