前回Scilab様にIIRフィルタ設計をお願いできるようになったので、今回は実際にテキトーな信号をフィルタしてみるべし、と思い立ってハマりました。実際にフィルタ処理で気になっていたことがあったのだけれど、それ以前にハマる原因が潜んでました。Scilab恐るべし?って、なんだかな~。
※「手習ひデジタル信号処理」投稿順 Indexはこちら
ハマったところのおまとめ
今回ハマったところは以下の2点です。
-
- 自前の変数とScilabでマクロ定義されている関数内で使用されている変数名が「被る」ことがあるみたい
- denom(), numer()という関数で、「式」の分母、分子を取り出せた筈とおもったら関数が廃止されていた
1が発覚したのは、前回IIRフィルタの設計用に作成した genIIRLPe という関数を使おうとしてエラーとなったことです。前回動作OKだったのに何故?ということで調べてみると、原因はFIRフィルタの設計用に作成した genFIRLP2 関数と「併用」していたことにありました。
coeff = genFIRLP2(Fs, 100, 355, 0);
上記のようにFIRフィルタは、係数ベクトルを coeff という「ありがちな」お名前のベクトル変数に代入しております。この場合、自前のgenIIRLPe関数内で呼び出している Scilabの iir 関数から呼び出している bilt 関数内の coeff という変数と競合している、というのが観察結果っす。
変数を一度クリアして、以下のようなバッティングしそうにない変数名にしたらすらりと動きました。
coeffFIR = genFIRLP2(Fs, 100, 355, 0);
ううむ、Scilabにも変数スコープの global、localの区別はあり、また変数名を列挙する関数などもあるのでありますが、どうも当方が頭の中に思い描いていた挙動とは違うみたい。。。この問題についてはまた今度調べてみないとダメだな。
今回は当たり障りのない変数名に変更して「チラシ」ておきます。
※2023年1月15日追記、上記の問題に関する調査結果はこちら
第2のdenom(), numer()の件は、手元のScilabバージョン 6.1.1 のHelpファイルから以下の2行を引用させていただきましょう。
denom was obsolete and has been removed. Please use the .den rational field instead.
numer was obsolete and has been removed. Please use the .num rational field instead.
廃止になっちゃたのね。そして denom(チョメチョメ)ではなく、チョメチョメ.den などとせよ、ということなのね。動かん、どしたらよいのと思って調べたら廃止されておったと。がっかりだあ。
自分で気になっていた部分
まずは過去回で作成済のサンプリング周波数Fsの具体値を指定して、実際の周波数(正規化周波数でない)で記述できる自前の「ラッパ」関数群を呼び出しておきます。
exec('scilab\genSin.sce'); exec('scilab\plotFFT.sce'); exec('scilab\genFIRLP2.sce'); exec('scilab\genIIRLPe.sce');
genFIRLP2が第65回で作成済のFIRフィルタの設計用関数、genIIRLPeが第66回で作成済のIIRフィルタの設計用関数です。
次数は大幅に異なりますが、サンプリング周波数10kHz、カットオフ周波数 355Hz、周波数特性表示なし、ということでフィルタを作成したところが以下に。
Fs=10000; coeffFIR = genFIRLP2(Fs, 100, 355, 0); hzIIR=genIIRLPe(Fs, 6, 355, 0.5, 40, 0);
さて、ここで気になっていたのは、genFIRLP2関数は、係数(実数)を並べたベクトルを返し、genIIRLPe関数は、zを使って記述された伝達関数を返してくることです。こんな感じ(FIRの方は係数100個あるので、最初の10個だけ。)
みた目の形式がまったく違うのだけれど、実際に入力信号にフィルタを適用するfilter関数は処理できるのか?というのが気になっていたことであります。
実際にIIRフィルタの伝達関数でフィルタ処理してみる
FIRフィルタ式の係数ベクトルを使って処理できることは第64回にて既に実験済です。そのときと同様なフィルタ処理を行ってみることにいたしました。
まず、テスト用の入力信号の作成。今回はカットオフ周波数 355Hz ということで入力信号は、277Hzと777Hzのサイン波を加えてみました。こんな感じ。
[t, x277] = genSin(Fs, 4096, 277, 0, 1); [t, x777] = genSin(Fs, 4096, 777, 0, 1); x = x277 + x777;
さて、filter関数は、分子、分母の係数列を受け取るようになっています。先ほどみたとおり、denom(), numer()関数廃止のため、.den、.num使えということであったので単独で使ってみました。
確かに分子、分母とりだせるけれども、zを使った表現で係数ベクトルじゃないです。大丈夫なのか?
入力信号 x をフィルタにかけてみます。えい!
[yI, zfI] = filter(hzIIR.num, hzIIR.den, x);
入力信号 x とIIRフィルタ後の出力信号 yIをプロットしてみると
plotFFT(Fs, x, 1); xtitle("Input Signal 277Hz + 777Hz, FFT"); plotFFT(Fs, yI, 1); xtitle("Output Signal 277Hz + 777Hz, filter=IIR, FFT");
プロット結果をならべるとこんな感じ。左入力、右出力。
277Hzはしっかり残っているけれど、777Hzはほぼほぼ消えてます(縦軸リニアなので。)z使った表現で与えてもちゃんとフィルタはしてくれるみたい。