はじめに
大学で無線通信の信号処理に関する研究をしていまして,
今年1年間でD言語で無線機のシミュレータを開発しました.
今回は,シミュレータを開発するにあたって,すごく便利だったD言語の機能だったり,知見を共有できたらなと思います.
Range
信号処理は,
- ソース: 信号の源(正弦波,ランダムなビット列,白色雑音)
- シンク: 信号の出力先(信号のスペクトルの描画)
- 処理ブロック: 信号処理ブロック(アンプ,ミキサ,フィルタ)
というような基本的な3つの要素で構成できます.
ソースや処理ブロックはInputRangeで簡単に記述でき,シンクはOutputRangeで表現できます.
Rangeのわかりやすい説明やその他の応用については,同じく今年のD言語 Advent Calendarでumariderさんとlempijiさんの記事があります.
- D言語で今始めるRange: umarider
- OutputRangeの定義と使い方: lempiji
- D言語で今楽しむRange: umarider
- OutputRangeの利用と応用(OutputRangeからReactive Extensionsへ至る道): lempiji
Rangeを利用することによって,標準ライブラリで提供されている機能をそのまま利用することができます.
たとえば,信号を定数倍するだけのアンプであればstd.algorithm.map
を使えばいいですし,
信号をあるサンプル数だけ進めるのであればstd.range.popFrontN
やstd.range.drop
を使えば簡単です.
終わりのない無限の長さを持つ信号も表現でき,std.range.take
でその長さを制限することもできます.
また,UFCSによって信号処理の流れが非常にわかりやすくなります.
immutable real freq = 1e6, // sin波の周波数1MHz
ts = 1.0/(20e6), // サンプリング周波数 20MHz
amp = 2.0; // 増幅率
// sin波の生成, InfiniteRange
auto sinsig = sequence!"n * a[0]"(2 * PI * freq * ts)
.map!(a => sin(a) * amp);
FFT
無線通信の信号処理では,いたるところにFFTが出現します.
たとえば,WiFiなどの無線通信で使われているOFDMという変調方式は,FFTを用いて信号を生成しています.
D言語では標準ライブラリのstd.numeric
というモジュールにFFTが用意されています!!
もちろん,Rangeインターフェイスに対応しています.
Traits(Concepts)
正式名称は良くわかりませんが,D言語ではよく次のようにisXXXX
というテンプレートを定義して,型の分類を行います.
たとえば,RangeにもisInputRange
だったりisForwardRange
などがあります.
無線通信の信号処理では,変復調器のように,インターフェイスが決まっているものがあります.
例の変復調器では,modulate
やdemodulate
というメンバ関数だったりを要求するisModulator
を定義しています.
enum bool isModulator(T) = is(typeof((T mod){
// 変調器への入力の型,ubyte(ビット列), Complex!floatなど
alias IType = T.InputElementType;
// 変調器からの出力の型,Complex!floatなど
alias OType = T.OutputElementType;
IType[] inputs;
OType[] outputs;
outputs = mod.modulate(inputs); // IType -> OType
inputs = mod.demodulate(outputs); // OType -> IType
}));
std.parallelism
複数のパラメータでのシミュレーションを並列して実行したくなったときに,D言語であれば標準ライブラリのstd.parallelism
モジュールを使えば簡単に並列化できます.
たとえば,送信電力を10mW(10dBm)から100mW(20dBm)まで掃引してシミュレーションしたい時,並列化は次のように行えます.
import std.parallelism : parallel;
// 内部が並列実行される
foreach(power; iota(10, 22, 2).parallel)
{
runSimulator(power);
}
単体テスト
最初の方に述べた通り,無線通信信号処理では,ブロック単位でプログラムが独立します.
そのため,必然的にモジュール間や関数間で高い直交性を持ったプログラムとなり,単体テストによる各関数のテストが容易です.
D言語では,次のようにいろいろな場所に単体テストを書くことができます.
もし,パッケージマネージャのDUBを使っていれば,dub test
だけで単体テストを走らせることができます.
// 増幅器
auto amplified(R, F)(R input, F amp)
{
return input.map!(a => a * amp);
}
unittest
{
assert(iota(3).amplified(2).equal([0, 2, 4]));
}
C言語のライブラリが簡単に使える
無線通信の信号処理といえど,よく行列やベクトルの計算(固有値問題とか)が出現します.
そういうときに,BLASやLAPACKを触れるとすごく便利です.
D言語では,C言語のライブラリを簡単に使うことができるので,BLASやLAPACKも使えます.
また,私の在籍する大学には,小規模ながらクラスタ計算機が設置されており,MPIを用いて超並列計算もできます.
各ノードはIntelのXeonを搭載しているので,D言語も環境を構築すれば動かすことができます.
そこで,実際にOpenMPIを用いたライブラリも作成しました -> TUT-HPCLIB4D.
まとめ
信号処理するならD言語しかない!!