panda's tech note

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

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

Day 19: 双方向通信のための準備

今日から数日間で双方向通信を実装します。

受信シンボルのフレーム構造分析

17日目 で実装した demodulate() 関数で変調したデータを物理層・データリンク層のフレームとして処理します(データが受信・復調できて忘れてました)。

以下の物理層の定義のうち, demodulate() 関数により得られるシンボル列はプリアンブル・SFD以降なので,48ビット分が物理層のフレームとしてシンボル列に含まれます。

+------------+------------+-------------+------------+------------+-----------+
| Preamble   | Preamble   | Modulation/ | Reserved   | Length     | CRC16     |
| (SYNC)     | (SFD)      | Code rate   |            |            |           |
+------------+------------+-------------+------------+------------+-----------+
 128 symbols  16 symbols   8 bits        8 bits       16 bits      16 bits

上記の物理層の信号に以下のようなデータリンク層のフレームが続きます。

+-------+---------+-------------+-------------+----------+---...---+-------+
| Frame | Symbols | Destination | Source      | Sequence | Data    | FCS   |
| type  |         | address     | address     | number   |         |       |
+-------+---------+-------------+-------------+----------+---...---+-------+
 1 byte  2 bytes   4 bytes       4 bytes       2 bytes              4 bytes

このデータ構造を Python のクラスとして以下のように定義し,シンボル列からフレームの各パラメータを取り出す parse() メソッドを実装します。物理層のModulcation/Code rateにより,データリンク層の処理を変更できるように設計していますが,今回は簡単のため物理層とデータリンク層を同じクラスに実装しました。

"""
PHysical & data link layer protocol class
"""
class MyProtocol:
    """
    Constructor
    """
    def __init__(self):
        self.modulation = None
        self.length = 0
    """
    Parse symbols to get a frame
    """
    def parse(self, data):
        # Physical layer
        self.modulation = data[0:8].int
        self.length = data[16:32].int
        # Check the checksum∫
        if not crc16_check(data[0:48].bytes):
            return False
        # Data-link layer
        self.frame_type = data[48:56].int
        self.symbols = data[56:72].int
        self.destination = data[72:104].int
        self.source = data[104:136].int
        self.sequence_number = data[136:152].int
        if data.len - 48 < self.symbols:
            return False
        self.payload = data[152:16 + self.symbols].bytes
        # Check the checksum
        if not crc32_check(data[48:48 + self.symbols].bytes):
            return False
        return True

18日目 までに実装した

0x010000a84c460000a80000000200000001000161626364d77a99cf

を以下のように使用します(上記 MyProtocol クラスの定義と crc16/32 関係の関数(11日目を参照)は省略しています)。

import bitstring
import crcmod

# Class定義
# crc16, crc16_check, crc32, crc32_check の実装

data = bitstring.BitArray(hex='010000a84c460000a80000000200000001000161626364d77a99cf')
protocol = MyProtocol()
if not protocol.parse(data):
    print("Failed to parse the protocol data")
print(protocol.payload)

上記を実行すると以下のように送信されたデータが復元できます。

b'abcd'

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

今日はオリジナルプロトコルの構造を解析するために,MyProtocol クラスを実装しました。明日はSDR機器を無線インターフェイスとして扱うためのクラスを実装します。