2次のIIRでローパスフィルタを作ってみる
そろそろ実装しないとなーって思ってて、
復習がてらグラフも描いてみた。
use v5.14; use strict; use warnings; use Math::Trig qw( tan pi ); use Imager; my $cutoff = 0.01; my $q = 1.0 / sqrt(2.0); my $type = 'Low Pass'; printf( "filter: type: %s, cutoff = %.2f, Q = %.2f\n", $type, $cutoff, $q ); my $fc = tan(pi() * $cutoff) / (2.0 * pi()); my $denominator = 1.0 + ((2.0 * pi() * $fc) / $q) + (4.0 * pi() * pi() * $fc * $fc); # 各係数の分母 my $b0 = (4.0 * pi() * pi() * $fc * $fc) / $denominator; my $b1 = (8.0 * pi() * pi() * $fc * $fc) / $denominator; my $b2 = (4.0 * pi() * pi() * $fc * $fc) / $denominator; my $a1 = ((8.0 * pi() * pi() * $fc * $fc) - 2.0) / $denominator; my $a2 = (1.0 - ((2.0 * pi() * $fc) / $q) + (4.0 * pi() * pi() * $fc * $fc)) / $denominator; printf( "b0 = %.4f, b1 = %.4f, b2 = %.4f, a1 = %.4f, a2 = %.4f\n", $b0, $b1, $b2, $a1, $a2 ); my $step = 0.01; my $tmp = (2.0 * pi()) * $step; my @src = map { sin($tmp * $_); } 0..511; my $img = Imager->new( xsize => scalar(@src), ysize => 500 + 1 ); $img->box( filled => 1, color => 'white' ); draw_graduation( $img, 'black' ); draw_wave( $img, \@src, Imager::Color->new(0, 255, 0) ); my ( $z_m1, $z_m2 ) = ( .0, .0 ); my @dst = map { my $in = $_ - (($z_m2 * $a2) + ($z_m1 * $a1)); my $ret = ($z_m2 * $b2) + ($z_m1 * $b1) + ($in * $b0); ( $z_m2, $z_m1 ) = ( $z_m1, $in ); $ret; } @src; draw_wave( $img, \@dst, Imager::Color->new(255, 0, 0) ); $img->write( file => "graph.png" ) or die $img->errstr; sub draw_graduation { my ( $img, $color ) = @_; my $y0 = int($img->getheight() / 2); my $step = 100; my $w = $img->getwidth(); my $h = $img->getheight(); my $x = 0; while ( $x < $w ) { $img->line( color => $color, x1 => $x, y1 => 0, x2 => $x, y2 => $h - 1 ); $x += $step; } $img->line( color => $color, x1 => 0, y1 => $y0, x2 => $w - 1, y2 => $y0 ); } sub draw_wave { my ( $img, $data, $color ) = @_; my $y0 = int($img->getheight() / 2); my $ymax = $y0; my $xmax = scalar(@{$data}) - 1; my @points = map { [ $_, $y0 - int($ymax * $data->[$_]) ]; } 0..$xmax; $img->polyline( points => \@points, color => $color ); }
これを実行すると、こんな感じになる。
$ perl aaa.pl
filter: type: Low Pass, cutoff = 0.01, Q = 0.71
b0 = 0.0009, b1 = 0.0019, b2 = 0.0009, a1 = -1.9112, a2 = 0.9150
そんなことより、Imager
でラインに透過度を割り当てても、
思ったように透過が施されなくて困った。
試しに、Imager
オブジェクト生成時にchannels => 4
にしてみたけど、
黒で描いた目盛に対して、ラインの透過がうまく反映されない。
重なった部分がやや暗くなって欲しいんだけど、透過していない。
仕方ないので、破線で目盛を描こうと思ったけど、
破線ってどうやって描くの???
線の太さって指定できないんだっけ???
ってなって、うーん・・・。
なんか、つらいですね。
おしまい。
Leave a Comment