HackRF One 用の sweep mode を利用するスペクトラムアナライザーで、いまいち安定して動くものが見つからなかった。WebUSB で書いてブラウザだけあれば動くようにしたら便利かと思い書いてみた。
余計なものをインストールせず、ブラウザで上のURLを開くだけで使えるので普通に便利。だけど WebUSB は今のところ Google Chrome でしか使えない。
実装
HackRF との通信
元々 libhackrf (Cのライブラリ) は libusb を使って実装されている。WebUSB も libusb のほぼそのままの API のラッパーなので、読みかえて実装すれば簡単に移植できる。面倒なのは実際に transferIn() するところぐらい。
USB 帯域を使いきる
20MHz サンプルで 8bit I/Q 信号を得るので、コンスタントに40MB/sec の転送帯域を使用する。これは USB 2.0 の実効速度のほぼ上限にあたるため、気をつけて実装しないと抜けが発生する。
追記:20MB/sec 使いきるのは通常のサンプリングの場合で、スイープモードの場合はブロックサイズの関係上 13.3MB/sec ぐらいになる。
WebUSB だと transferIn() を使って転送するが、素朴に await transferIn() を繰替えすと必ず取りこぼしが発生した。このため、2つの await transferIn() を同時にループさせて、いずれかの IO で常にデータを受信できるようにしてほぼ解決した。
これでもマシン負荷によっては時々とりこぼす (とりこぼしたところは白くなる)
WebWorker で WebUSB を使う
WebWorker 内で WebUSB は使える。使えるけど、事前に requestDevice() してペアリング (USBにはない概念) しておく必要がある。requestDevice() はメインスレッドでユーザインタラクションがないと呼べない。
- メインスレッドで requestDevice() して device を取得してペアリングする
- 取得した device 情報を worker に postMessage (device を直接 postMessage することはできない)
- ワーカースレッドで getDevices() してペアリング済み device 一覧を取得して、マッチするものを選ぶ
という手順が必要だった。
FFT する
FFT は RustFFT の実装を WebAssembly で呼んでいる。ComplexAnalyserNode (WebAudio) を作った (IQ信号のFFT) | tech - 氾濫原 とほぼ似たような実装。
ウォーターフォール表示
2D 描画でも WebGL を使うべきか? スペクトラムウォーターフォール最速決定戦 | tech - 氾濫原 というのを前に書いたが、現時点だと WebGL 実装が一番負荷が少なかったので、WebGL を使って実装した。
一応 WebGL 使わないバージョンと切り替えて使えるようにしている。