Raspberry Pi Picoが届いたので音響通信はじめました。
スイッチサイエンスさんからRaspberry Pi Picoが届いたので、準備しておいたTBSKmodemMicroで音響通信(送信のみ)を試しました。
事前実験に協力してくれたたけぞうさん、有難うございます。
実験結果
✨なんとか成功✨
らずぴこのMicroPythonからすまぽのWebaudio版Tbakmodemに音響データ通信できた。これでオムロンのあれが作れる。 pic.twitter.com/AltrbjGtCN
— にゃとらん&&🍀 (@nyatla) 2023年2月14日
当初の目標であった16kHzでの転送は処理が間に合わず断念しましたが、8kHzでの転送はなんとか成功しました。
最適化したら16kHz(160bps)でも送信できました。
距離は15cm以内と短距離です。そこそこのエラー率でデータの送信ができています。
実験環境
15cm程度の近距離で、他のTBSKmodemで信号を復調できることを確認します。別にスマートフォンやPC+マイクなどの受信装置が必要です。受信装置には、TBSKmodemのライブデモのほか、Processing、C#、Pythonの実装を使用できます。
ハードウェア
ハードウェアは安くて頑丈で使いやすい、RaspberryPi Picoとパッシブ圧電素子です。(圧電素子はオムロンさんが通信機能付き体温計でも動作実績があり、安心です。)
Raspberry Pi Pico、圧電素子、ダイオードはクランプ回路なのでお好みでいれてください。
ソフトウェア
ソフトはMicroPythonのみで記述し、出力は古典的なGPIOのON/OFF制御のみを使います。
この条件は同期や信号品質が重要なアナログ信号モデムにはなかなか厳しいものですが、これで動くならどこでも動くだろという軽い気持ちで選定しました。
gistにあるコードをThonnyにコピーすれば実行できます。
ChatGPT先生によるマニュアル
みんな大好きchatGPTさんにマニュアルを書いていただきました。
このコードは、MicroPythonで実行される純粋な音響データ送信サンプルコードです。RP2040のGP2にブザーかスピーカーを接続することで、TBSKmodemによって受信されます。このコードでは、以下のクラスが定義されています。
- BitArray: ビットを格納するイテレータ
- PnBits: 長さがsizeのPNトーンを生成するクラス
- RawBits: bit配列からBitArrayを生成するクラス
- TbskMicroModulator: TBSK変調した矩形波パターンを生成するクラス
- TbskMicroModulatorは、TBSK変調を実行するために必要な矩形波パターンを生成するためのクラスです。_preambleと_tone属性によって、前置ビット(プリアンブル)と変調されるPNトーンが生成されます。
moduleteメソッドは、変調されたデータのビット配列を生成するジェネレータを返します。G関数内で、プリアンブルの出力、データビットの出力、そして接尾辞の出力(直接出力なら不要)が行われます。
tx関数は、出力ピンを介して音声信号を送信するために使用されます。ビット列の各ビットを出力ピンに書き込むことで、音声信号が生成されます。INTERVAL_USは、8kHzの周波数で出力するための間隔です。
最後の2行は、TbskMicroModulatorオブジェクトを作成し、音声信号を送信するためのビット配列を生成し、tx関数を使用して信号を送信するためにコードを実行しています。
コードを入力するだけでここまで書いてくれるなんて。全く凄い時代になりました。
技術周りの話
TBSKmodemMicroは、最低限のTBSK変調の規格を満たす信号を最小のコード量で実装することを目的としました。
生成される信号の互換性は保たれていますが、通信距離は短く、信号の品質は悪く、雑音耐性もほぼありません。
実装後に残った最低限のコードは100行以下となり、やや残念な気持ちです。
改めてコードを見直すと、なるべくメモリを使わないように、ビット列をN倍(100倍)に展開して送信するものであることがわかります。
送信部分の出力レートは、変調処理に時間がかかり、8kHzが限界でした。ギリギリ16kHzが出力できました。これ以上の搬送速度を出すためには、C言語化やインラインアセンブラが必要になると思います。
コードの中には、いくつかマジックナンバーがあります。これらのマジックナンバーは、シンボル同期のための調整定数です。別の機材で実行するときには、実測に基づき、調整しなければなりません。
95,97行目は、シンボル長誤差を吸収するための調整定数です。TBSK変調はシンボル同期通信なので、シンボル長の誤差が±1%なければなりません。しかしMicroPythonは10us以下の制御は不可能なので、Tick間隔(8kHzの場合125us)の誤差を10%まで許容し、累積値を基準にして、シンボル長の誤差を1%以内に制御するようにしました。この方式は、MicroPythonの予測不能な実行時間対策にも有効です。
87行目の-1は、1を引くといい感じにタイミングが合うというよくわからないアレです。95と97で吸収するよりも効果があるのでこの場所に存在します。このマジックナンバーは不要になったので廃止しました。