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() はメインスレッドでユーザインタラクションがないと呼べない。

  1. メインスレッドで requestDevice() して device を取得してペアリングする
  2. 取得した device 情報を worker に postMessage (device を直接 postMessage することはできない)
  3. ワーカースレッドで getDevices() してペアリング済み device 一覧を取得して、マッチするものを選ぶ

という手順が必要だった。

FFT する

FFT は RustFFT の実装を WebAssembly で呼んでいる。ComplexAnalyserNode (WebAudio) を作った (IQ信号のFFT) | tech - 氾濫原 とほぼ似たような実装。

ウォーターフォール表示

2D 描画でも WebGL を使うべきか? スペクトラムウォーターフォール最速決定戦 | tech - 氾濫原 というのを前に書いたが、現時点だと WebGL 実装が一番負荷が少なかったので、WebGL を使って実装した。

一応 WebGL 使わないバージョンと切り替えて使えるようにしている。

  1. トップ
  2. tech
  3. WebUSB HackRF One スペクトラムアナライザー

hackrf_sweep コマンドの動作を調べたので記録しておく。このコマンドは HackRF One のファームウェアと協調して、うまく広い帯域をスイープできるように作られている。

まずいくつか定数がある

  • サンプリング周波数は 20MHz
  • フィルタ周波数は 15MHz
  • オフセット周波数は 7.5MHz (設定した周波数の7.5MHz上の周波数になる)
  • step_width は 20MHz
  • interleaved モード

とにかく interleaved モードを理解しないといけない。interleaved ではないモード (linear) は単純に step_width を足していくだけなので単純明快だが、interleaved ではファームウェア側は +5MHz、+15Mhz を繰替えしてスイープする動作をする。

これで何が嬉しいか?というと、いくらか効率を犠牲にして (スキャン時間や計算量が2倍) フィルタの減衰がかかる両端の領域や、DCオフセットがある中央の領域を捨てて綺麗な帯域だけからスペクトラムを合成できる。

詳しい動作

グラフをかいてみた。中央が0Hzで±10MHz (サンプリング周波数20MHz) の周波数領域を示している。全体の赤い領域が1回でとれる 20MHz サンプル、15MHz フィルタされた信号の全体で、青の領域と紫の領域だけを採用してさらにズラしていく。次は5MHzずらせば真ん中にある隙間と次の5MHz分が埋まり、その次は15MHzずらせば……と続く。

見ての通り帯域の端のほうではローパスフィルタ(アンチエイリアシングフィルタなので必須)による減衰の影響をうける ので避けたいし、0HzにはどうしてもDCオフセットが乗るので避けたい。

一回のサンプルで20MHzの帯域にうち、10MHzぶんの帯域しか取得しないが、全領域でできるだけフラットでノイズのない結果を得られる。

  1. トップ
  2. tech
  3. HackRF One の Sweep Mode の動作


HackRF という SDR 用の無線フロントエンドがある。オープンソースハードウェア。この手のものは他に LimeSDR (高い) や RTL-SDR (安い) などがあるが、HackRF は最高周波数が 6GHz までという点とコスパが良い点が大きな特徴。

アルミケース・TCXOモジュール・ロッドアンテナ付きで $125 ほど。

TCXO モジュールはインストールされていないので、一旦ケースを分解して装着する必要があった。適当にググって解決。

brew でツールが入る

brew install hackrf

TCXO がちゃんと認識されているかはこれでわかる (ref. HackRF One · mossmann/hackrf Wiki · GitHub

hackrf_debug --si5351c -n 0 -r 
[  0] -> 0x01 # 認識されている場合
[ 0] -> 0x51 # 認識されていない場合

これらのツールはあくまでCUIツールなので、GUI で何かするためには他ものがいる。Mac だと Gqrx はデフォルトで HackRF の入力に対応しており復調もできる。

スペクトラムアナライザーっぽく使えるのだとqspectrumanalyzerが一応使える。ただし非常に重く、よく落ちてしまう。

アンテナポートへの電源供給

GPS アクティブアンテナのように電源供給が必要な場合、アンテナポートに電源供給することができる。関連ツールにはコマンドオプションがある。

スイープモード

スイープモードというのが実装されている。HackRF One のハードウェア的には 20MHz サンプルなので、20Mhz 分の帯域しか同時に見ることはできないが、連続して局発周波数などを動かしてサンプリングしていくことで、擬似的に広い範囲の帯域を見ることができる。広域スペアナ的に使えるといえばわかりやすいか。

詳しい動作はまた今度説明したい。

TX

送信もできるけど HackRF One 本体からの出力は最大でも 15dBm (0.03W) なので、外部にリニアアンプなどが必要になる。

が、そもそもこの機械の仕様でアマチュア免許などは下りないと思うので、ダミーロードつけて微弱無線局として遊ぶしかないと思う。とはいえ、微弱無線局の定義も非常に厳しいので、基本的にはもったいないけど、国内ではおかざりと思ったほうが良い。

ソースコードの歩きかた

HackRF はオープンソースなのですべてのコードを読むことができる。かなり綺麗に書かれているほうだと思うので、すんなり読みくだせるものが多い。

基本的にツール側から攻めていくのがわかりやすそう

これ以下に hackrf_info などのツールのソースコードがある。

ツール類はほぼ libhackrf を呼んでいて、こちらも同じレポジトリ内にある。難しいことはあまりしておらず、素直に libusb の関数を呼びだしているので読みやすい。機能もたくさんあるわけではない。

ファームウェアは若干複雑ではあるが、USB の API から攻めていくのが読みやすい気がする。

cpld (FPGA の弟みたいなやつ) まわりはまだちゃんと読めてない。

  1. トップ
  2. tech
  3. HackRF One で遊ぶ