2017年5月28日日曜日

デジタルフィルタ MicroModeler DSPを使ってみる

デジタルフィルタは、リズムマシンを作った時PSoC5 LPのDFBを使ったことがある。(参考:「PSoC 5LPのデジタル・フィルタを使ってみる」「PSoC 5LPのデジタル・フィルタを使ってみる CPUで波形生成」)

PSoC 5LPのDFBはPSoC CreatorのFilterコンポーネントを使うとかんたんにデジタルフィルタを実装できるが、コアがCortex-M3なので浮動小数点演算がなかなかしにくい。(もうすぐPSoC6(Cortex-M4 & Cortex-M0+)が出そうなので期待しています。)

デジタルフィルタの設計ツールとして「MicroModeler DSP」というツールを見つけたので試してみた。コミュニティ版は非商用なら無償で使える。

デジタルフィルタの種類


「音遊び!Blackfin DSP基板でディジタル信号処理初体験」の付録についているAnalog DevicesのBlackfinを使ったボードでデジタルフィルタを試したことがある。



この本はDSPの使い方入門といった感じで、信号処理の理論的なことにはあまり触れられていないが、DSPとはいかなるもので、どうやってプログラミングすればいいのか?という取っ掛かりにはちょうどいいと思う。

DSPの実行環境はなかなかないので付録基板ですぐに試せるのが大きなメリット。

<追記:2017.05.31>
amazonの転売はふっかけすぎですが、marutsuでは新品を定価販売されているようです。
</追記>

最初の方の三章を使って、移動平均、FIR(有限インパルス応答)、IIR(無限インパルス応答)の説明がなされている。

これにしたがって、MicroModeler DSPで3つのフィルターを試してみた。

MicroModeler DSPではコードを自動生成してくれるので、Visual StudioでWindowsのコンソールプログラムを作り、出力をExcelでグラフ化してみた。

デジタルフィルタの方式が変わっても、インターフェイスは変わらないようなので、

#define LOOP    (2)
#define PERIOD  (50)

int _tmain(int argc, _TCHAR* argv[])
{
    filter1Type* filter = filter1_create();

    printf("Sample\tInput\tOutput\n");
    int count = 0;
    for (int i = 0; i < LOOP; i++) {
        float in = 1.0f;
        for (int j = 0; j < PERIOD / 2; j++) {
            filter1_writeInput(filter, in);
            float out = filter1_readOutput(filter);
            printf("%d\t%f\t%f\n", count, in, out);
            count++;
        }
        in = 0.0f;
        for (int j = 0; j < PERIOD / 2; j++) {
            filter1_writeInput(filter, in);
            float out = filter1_readOutput(filter);
            printf("%d\t%f\t%f\n", count, in, out);
            count++;
        }
    }

    return 0;
}

というコードで矩形波を入力してフィルタ応答を出力してみた。

移動平均


一番かんたんなLPFで実装が楽で高速。パラメータは前の値を保持しておくバッファ長のみ。


横軸が時間で、縦軸が出力値。かんたんなだけあって過渡応答も線形。

FIR(Lth Band Filter)


フィードバックなしのデジタルフィルタ。フィードバックしないので安定的で発振しない。カットオフ周波数がサンプリング周波数の1/Nに限定される。


スマホのファンクションジェネレータ等、デジタル機器の矩形波出力によく似た応答だと思う。

IIR(バターワース特性)


フィードバックありでアナログフィルタに近い応答をする。設計をミスると発振する。


Orderが4の場合の過渡応答で、4次LPFに相当すると思う。FIRに比べるとアナログ回路で見慣れた波形。

Orderを1にしてみる


Orderを1にした場合は、多少きれいすぎるがCRの1次LPFの過渡応答にそっくりさんだ。

Github:
https://github.com/ryood/DigitalFilterTest

少し触っただけだが、デジタルフィルタについてほとんど知識がなくても使えた。←画面に出てくる「Z-Plane」や「Transfer Fuction」が何なのかわからないレベル(^q^;

ゼロや極で特性を決めることもできるようだ。←これは制御工学でやったので名前だけは知ってるレベル(^q^:

生成されるコードは、標準Cの他に、ARMのCMSISのライブラリを使ったものも出力できる。(IIRだとオプションでBuiquadsをDirect Form1にしてやる必要がある。)

「ARM + CMSIS」で生成されたコードを見ると

float32_t filter1_coefficients[10] =
{
// Scaled for floating point

    0.0979448726126829, 0.1958897452253658, 0.0979448726126829, 0.7973802425733677, -0.17423867505153404,// b0, b1, b2, a1, a2
    0.125, 0.25, 0.125, 0.8219434348269887, -0.34174007022989955// b0, b1, b2, a1, a2

};

//<中略>

 void filter1_init( filter1Type * pThis )
{
    arm_biquad_cascade_df1_init_f32(    &pThis->instance, filter1_numStages, filter1_coefficients, pThis->state );
    filter1_reset( pThis );

}

//<中略>

 int filter1_filterBlock( filter1Type * pThis, float * pInput, float * pOutput, unsigned int count )
{
    arm_biquad_cascade_df1_f32( &pThis->instance, pInput, pOutput, count );
    return count;

}

となっていてフィルターの演算はCMSISにまかせている。もしかしたらDSP付きのARMならDSPを使うようになってるのかもしれない?

また、標準Cでもfloat型だけではなく整数型(固定小数点型)のコードも出力してくれるようだ。FPUが付いていないCoretex-M0やM3で演算速度を上げたい場合にも使えるかも?

メモ:


デジタル的な波形をアナログチックにするために、(1- e^(-t/τ))みたいな関数を使って変換しようかと漠然と考えていたが、この用途にはIIRが使えるかもしれない。

多分IIRの方が速度的に有利な気がする。積分を数式で解くのはしんどいが、コンピューターを使って数値積分すれば近似的に解けるみたいな。

エンベロープ波形をMPU内部でアナログチックにするのに一度使ってみようか?