2014-09-13

VS2013 C# でlibrtlsdr.libを使ってみる

DS-DT310をSDR#でいろいろ受信してみたんですが、そろそろ飽きてきたので、なにかコードを書いてみようかと思ったのですが、とりあえず、C#から使ってみようということです。
さすがにDSPのコードをC#で書こうということはしませんが、簡単な処理なら多少非力なCPUでも処理できるのではないかと思います。

[PR] 楽天でDS-DT310をチェック

librtlsdr.libを用意する

まずは、osmocomのwikiに"pre-built Windows version"というリンクがあるので、そこからlibrtlsdrが入ってるzipファイルをダウンロードします。​そこにx64とx32のフォルダがあり、その中にあるDLLが必要です。
あと、rtl-sdr.hというファイルがありますが、このヘッダを参考にDllImportしますので、これも必要です。

VS2013のC#プロジェクトの設定

まず、普通にC#でプロジェクトを作成します。このプロジェクトにlibrtlsdrのDLLを追加するのですが、x64かx32のどちらか一方だけを追加します。
追加したDLLのターゲットにあわせて、プロジェクトの構成マネージャーを変更します。VSのツールバーに"x32"とか"Any CPU"を選ぶところがあるので、そこで変更できます。

あと、このまま、ビルド、デバッグするとDLL見つかりませんといわれてしますので、ビルド後にDLLを実行するフォルダにコピーするようにします。
プロジェクトのプロパティにビルドイベントいうのがあり、「ビルド後に実行するコマンドライン」という項目に以下のコマンドを入れればよいです。

copy "$(ProjectDir)*.dll" "$(TargetDir)"

DllImportの定義

C#からlibrtlsdrのDLLにある関数を呼び出すためにDLLImportをつかってC#のメソッドといて呼びさせるように定義します。
以下にDllImport定義の一部です。これを参考に必要な関数も定義してください。

[DllImport("rtlsdr.dll", CharSet = CharSet.Ansi)]
extern static int rtlsdr_get_device_count();
[DllImport("rtlsdr.dll", CharSet = CharSet.Ansi)]
extern static IntPtr rtlsdr_get_device_name(int index);
[DllImport("rtlsdr.dll", CharSet = CharSet.Ansi)]
extern static int rtlsdr_open(out IntPtr dev, int index);
[DllImport("rtlsdr.dll", CharSet = CharSet.Ansi)]
extern static int rtlsdr_close(IntPtr dev);
[DllImport("rtlsdr.dll", CharSet = CharSet.Ansi)]
extern static int rtlsdr_reset_buffer(IntPtr dev);
[DllImport("rtlsdr.dll", CharSet = CharSet.Ansi)]
extern static int rtlsdr_read_sync(IntPtr dev, IntPtr buf, int len, out int n_read);
[DllImport("rtlsdr.dll", CharSet = CharSet.Ansi)]
extern static uint rtlsdr_get_center_freq(IntPtr dev);
[DllImport("rtlsdr.dll", CharSet = CharSet.Ansi)]
extern static int rtlsdr_set_center_freq(IntPtr dev, uint freq);

【追記】.Net4以降では、DllImportの中に"CallingConvention=CallingConvention.Cdecl"を追加してください。

librtlsdrの関数の使い方

rtlsdr_get_device_name()は、IntPtrを返しますが、これをSystem.Runtime.InteropServices.Marshal.PtrToStringAnsi()を使って、文字列に変更します(ただし、本当にANSIなのかはわかりませんw、とりあえずです)。
サンプルの読み込みは、非同期ではなく同期関数rtlsdr_read_sync()を使います。当然ですが、この関数はブロックするので、スレッドの内でループして読み込みようにします。また、rtlsdr_read_sync()に渡すバッファは、System.Runtime.InteropServices.Marshal.AllocCoTaskMem()を使って確保したほうがいいと思います。ちなみに送られてくるデータはosmocomのwikiに書いてあるとおり"8-bit I/Q-samples"です。
あと、 rtlsdr_open()を呼び出した後、rtlsdr_read_sync()を呼び出す前に、rtlsdr_reset_buffer()を呼んでおく必要があるようです。

おわりに

これでとりあえずは、librtlsdrの関数を呼び出すことができるようになったわけですが、あとはrtl-sdr.hのコメントやlibrtlsdrのソースコード(いくつかのコマンドのソースが含まれている)を見ながらコーディングしようと思ってます(まだ、細かいところまでは使ってない)。ちなみにlibrtlsdrのソースコードは、GitHubの中にあります。そこの右側にDownloadZIPというのがあるので、Gitがなくても入手できます。

ブログタイトルを変えました

最近、完全に放置してましたが、このところ、SDRというのが流行ってきるようなので、そちらをメインにやっていこうかと思います。
よろしくお願いします。

あと、私鉄空線に興味があるかたが、コメント頂いたのですが、こちらも放置してました。誠にもうしわけありません。空線デコーダーについては、どこかで公開したいかと思います。ただ、作ったのが相当前なので、最近の環境に合うかどうかはわかりません。