今回のアルゴリズムの式は以下であった。

この式で、簡略化のためにxp-xp-Nをxp、exp(j2πf/N)をkとし、またこの演算で発生する誤差をεp(複素数)として以下のように表す。

すると、この式は以下のように展開できる。

さらに、書き直すと、

となる。したがって、誤差成分は以下のようになる筈だ。

kはexp(j2πf/N)なので値域は-1.0~+1.0だ。 εは浮動小数演算の仮数部で発生する丸め誤差だが、大きさは指数部の値によって決まる。入力信号は16bit整数であり、これまでは、これをそのままの大きさで演算していた。この場合、指数値は2^-15から2^15の範囲となる。これを最大値(32768.0)で正規化すれば、exp()とスケール感が同じになるので誤差も小さくなるかも?と考えた。しかし、これは単に縮尺を変えているだけとも言えそうなのであまり違いはないのかも知れない。そこで、実際どうなるか、単一周波数のスペクトルで見てみた。
以下は正規化していない場合(ログ表示)
以下は正規化した場合
やはり、違いはなかった。
では、乗算や加減算で丸めをせずに切り捨てしたらどうなるかを見てみた。coregenで生成する演算器は丸めの有無を選択できないため、この確認の為には乗算器と加減算器を自作する必要がある。
乗算器は以下のようにした。

加算器は以下のようにした。

減算器(y=a-b)は、基本的に加算器のRTLを流用しb入力の符号を反転して使った。
結果としては、乗算器のみ丸め無し版に置き換えた場合は結果にほとんど違いがなかったが、加減算器の場合のはより悪くなり、スペクトル波形が激しく振動したような、無茶苦茶noisyな波形になってしまった。
ということで、誤差対策としては、縮小係数をかける方法と、一定回数毎にアキュムレーションRAMをクリアする方法のどちらかしか出来なさそうだ。他に変数の桁数を広げる(単精度小数を倍精度にする。)というのも考えられるが、回路規模がデカくなる、タイミング的(100MHz動作)に厳しそうだ、桁数を広げたとしても量子化された値での演算である以上は誤差は必ず発生し累積されてくる、等々考えて桁数を広げる案は止めにした。
では、2者の内どちらが妥協できるか?ということで、それぞれの場合の波形を比べてみる。
2048点毎にアキュムレーションRAMをクリアする方式の場合は以下のようであった。
見事に線スペクトルになっているように見えるが、上記はログ表示であり、これを絶対値表示(√r^2 + i^2)にすると以下のようであった。 (ちょっと変だ)
一方、縮小係数をかける方式の場合は以下のようであった。
ログ表示
絶対値表示
と、言うことで今回は縮小係数をかける方式で妥協しちゃおうかと考えている。
学生のときにもっと数学を勉強しとくんだった。 (;_;)
0 件のコメント:
コメントを投稿