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

20141002

そんなことより、Imagerでラインに透過度を割り当てても、
思ったように透過が施されなくて困った。
試しに、Imagerオブジェクト生成時にchannels => 4にしてみたけど、
黒で描いた目盛に対して、ラインの透過がうまく反映されない。
重なった部分がやや暗くなって欲しいんだけど、透過していない。
仕方ないので、破線で目盛を描こうと思ったけど、
破線ってどうやって描くの???
線の太さって指定できないんだっけ???
ってなって、うーん・・・。

なんか、つらいですね。

おしまい。

Leave a Comment