panda's tech note

Advent Calendar 2020: ソフトウェア無線

注意LimeSDR などのソフトウェア無線は技術基準適合証明(通称,技適)を受けていないため,これらの機器を日本国内で無線機として利用することは電波法により禁じられています。無線機として使用するためには,実験試験局免許を取得するか電波暗室・シールドボックスなどの設備を使用する必要があります。または,アンテナの代わりにケーブルとアッテネータを用い有線接続をし電波を発しないようにすることで,無線通信ではなく有線通信とはなりますが実験することができます(この場合も電波が漏れないように注意してください)。このページを参照される方は,実験される国や地域の法令などを遵守するようにご注意ください。また,実験等はご自身の責任でお願いします。

Day 16: オリジナルプロトコルの実装 (6)

今日は復調をして受信信号から送信されたデータの復元を行います。Python で実装している関係でリアルタイムでの復調が困難なため,プリアンブルを検出後無信号となるまでのシンボル列をパケットとして扱います。この機能を 15日目 に実装した detectEdge() 関数および detectPreamble() 関数を使って実装します。

手順としては,以下のアプローチで実装します。

  1. sdr.readStream() により受信サンプルを読み出す
  2. detectEdge() 関数により受信サンプル列からシンボルの境界(オフセット)を検出する
  3. サンプルの中央をシンボルの値として取り出す
  4. detectPreamble() 関数によりプリアンブルを検出する。プリアンブルが含まれていなければ 1 に戻る
  5. プリアンブルが含まれていた場合,現在のシンボル列を packetSymbols 変数にコピーする
  6. sdr.readStream() を呼び出し,手順3と同様にシンボルを取り出し,packetSymbols に追加する。無信号(シンボルの信号強度が RECEIVE_SIGNAL_THRESHOLD 以下であるもの)を含む場合,パケットの終了とする。パケットの終了でない場合は,この手順をパケットの終了まで繰り返す。

これを実装したコードは以下の通りです。

    while True:
        # Receive samples
        status = sdr.readStream(rxStream, [rxBuffer], rxBuffer.size)
        if status.ret != rxBuffer.size:
            sys.stderr.write("Failed to receive samples in readStream(): {}\n".format(status.ret))
            return False
        # Detect the edge offset
        samples = rxBuffer
        edgeOffset = detectEdge(samples, SAMPLES_PER_SYMBOL)
        if not edgeOffset:
            # No edge detected
            continue
        if edgeOffset + 4 >= SAMPLES_PER_SYMBOL:
            edgeOffset -= SAMPLES_PER_SYMBOL
        # Decode symbols from the center of a set of samples
        symbols = samples[range(edgeOffset + 4, samples.size, SAMPLES_PER_SYMBOL)]
        # Detect the preamble
        preamblePosition = detectPreamble(symbols)
        if not preamblePosition:
            # Preamble not detected
            continue
        print("Preamble detected")
        # Receive the samples while the signal is valid
        packetSymbols = np.copy(symbols[preamblePosition:])
        while True:
            status = sdr.readStream(rxStream, [rxBuffer], rxBuffer.size)
            if status.ret != rxBuffer.size:
                sys.stderr.write("Failed to receive samples in readStream(): {}\n".format(status.ret))
                return False
            samples = rxBuffer
            symbols = samples[range(edgeOffset + 4, samples.size, SAMPLES_PER_SYMBOL)]
            packetSymbols = np.concatenate([packetSymbols, symbols])
            if np.sum(np.abs(symbols) > RECEIVE_SIGNAL_THRESHOLD) != symbols.size:
                print("Packet end: # of symbols = {}".format(packetSymbols.size))
                break

今日のまとめと明日の予定

今日はパケットの開始から終了までのシンボル列を読み出すところを実装しました。明日はこれを復調してデータを取り出す部分を実装する予定です。