さて、インターポレータを実装したら次はデシメーターです。せっかくですのでポリフェーズ フィルタを使っていきたいところですが、その前によく考えてみる必要があります。
ポリフェーズフィルタを使ったデシメーションは確かに魔術的なアルゴリズムです。直感的に正しくないように見えますがきちんと動くところは、「こいつをどう実装してやろう」と心ときめかないこともありません。が、よく考えてみるとこの方法は今回はそれほど魅力的ではありません。
インターポレータの実装と同じく、デシメーターも入力バッファに複数サンプルを蓄積してそれをデシメーションするよう実装します。この場合、蓄積が前提なのでダウンサンプル比Mのデシメーションは次のような簡単な方法で実装できます。
つまり、はじめから間引かれるとわかっているデータに対しては畳み込みを行わずにディレイラインに突っ込むだけにし、Mサンプル目がきたらまじめに一回だけ畳み込みを実行するのです。このようないんちきができるのは、FIRフィルタが再帰的でないために使用しないデータをはじめから端折ることができるからです。反対にインターポレーターの場合は最初にアップサンプラが0をデータの間にはさみこむため、単純にデータを捨てることができず、ポリフェーズフィルタを使った変形が必要になったのでした。
ではポリフェーズフィルタによるデシメーターに何のメリットもないかというとそういうわけではありません。上の方法はMサンプルごとに一挙に大量の畳み込みを行いますので、各サンプル間の処理量が不均等になります。と、言うことは負荷が高くなるとMサンプル目の畳み込みを行っている間に次のデータがくる可能性があります。もちろん、待ってもらえばいいのですが、ハードウェアで行う場合には待ち行列をいくつも直列につなげてサイクルごとに不均等な処理をさせるのはあまり歓迎されません。そこで、1サンプルごとにN/Mタップのフィルタを行い出力側でM個のデータを足し合わせたほうが、負荷が均等に分散されるために回路が単純になると考えられます。
このように、同じアルゴリズムでもハードで実装するかソフトで実装するかによって向き不向きがあるのは面白いことです。今回はソフトなので単純な方法を使うことにします。
以下にアセンブリ言語で書いたプログラムを示します。今回はインターポレータの実装に比べるとやや簡単な上に、プログラム自身は前回のものを修正して使いましたので非常に短時間で出来上がりました。
最初の部分は今回も宣言です。この部分は11番目のパラメータがなくなったので少しだけ短くなっています。
#include <asm_sprt.h> .section/pm program; .global _downSamplerFunc; // void downSamplerFunc // ( // short input[], : first input data address // short inputModify, : distance of data ( dxxdxxdxx = 3 ) // short output[], : first output data address // short outputModify, : distance of adta // short count, : input data count to be processed // short ratio, : down sampling ratio // short delayLine[], : delayLine for FIR filter // short h[], : impuls response // short taps, : TAPs of FIR filter ( mutiple of ratio ). // short **curPtr, : current Pointer to delayline // short countDivRatio : count / ratio // ) #define input 1 #define inputModify 2 #define output 3 #define outputModify 4 #define count 5 #define ratio 6 #define delayLine 7 #define h 8 #define taps 9 #define curPtr 10 #define countDivRatio 11
続くお膳立ての部分もすこし変わりましたが、外部のバッファからデータを取ってきて出力バッファに結果を返す構造に変化がないため、この部分もインターポレータと大きくは変わりません。
// Register Allocation // i0,l0,b0,m0 : input, 0, , inputModify // i1,l1,b1,m1 : h, taps, h, 1 // i6,l6,b6,m6 : output, 0, , outputModify // i7,l7,b7,m7 : curptr*, taps, delayline, 1 // // mx1 : ratio-1 // my1 : taps-1 // Regsiters to be saved&restored // i7, m0, mstat // Registers to be restored to 0 // l1,l7 _downSamplerFunc: //******************** Stack frame entry; // establish the frame. pushs(i7); // save i7 pushs(m0); // save m0 pushs(mstat); // save mstat dis m_mode; // go into fractional mode. //******************** Initialization of pointer // set i0,l0,b0 & m0 = (input,0,-) & inputModify get_arg( i0, input ); get_arg( m0, inputModify ); // set i1,l1,b1 &m1 = (h,taps,h) & 1 get_arg_dreg( my1, h ); // my1 : h i1=my1; // i1 : h get_arg( l1, taps ); // l1 : taps m1=1; // m1 : 1 reg(b1)=my1; // b1 : h // set i7,l7,b7 & m7 = (*curPtr,taps,delayLine ) & 1 get_arg( i7, curPtr ); ax0=dm(i7+0); i7=ax0; // i7 : *curPtr get_arg_dreg( ax0, delayLine ); reg(b7)=ax0; // b7 : delayLine m7=1; // m7 : 1 get_arg(l7, taps); // l7 : taps // set i6,l6,b6 & m6 = ( output,0,-) & outputModify get_arg( i6, output ); // i6 : output get_arg_dreg( ax0, outputModify ); m6=ax0; // m6 : outputModify //******************** Set global get_arg_dreg( mx1, ratio ); ar=mx1-1; mx1=ar; // mx1 : ratio-1 my1=l1; ar=my1-1; my1=ar; // my1 : taps-1
実行部は当然大きくかわりました。外側のループはcount回ではなく、count/ratio回しか回りません。これは一回の繰り返しでratio個の入力をさばくからです。ループの冒頭で、ratio-1個のデータを問答無用でdelaylineに放り込みます。そうして、ratio個めの入力を入れてからtap回の畳み込みを行います。これによって、1/ratioのダウンサンプリングを行ったことになります。結果は出力バッファに格納します。
この関数に与える引数countは、ratioの整数倍でなければなりません。
//******************** Outer loop. acquire input "count" times get_arg_dreg(ax0, countDivRatio); cntr=ax0; do outerloop until ce; modify(i7+=m5); // (*curPtr)-- : m5 is always -1 //**************** dummy fill loop. cntr=mx1; // cntr = ratio-1 do dummyfill until ce; ax0=dm(i0+=m0); // get input. input++ by inputModify dummyfill: dm(i7+=m5)=ax0; // put input for delayline ax0=dm(i0+=m0); // get input. input++ by inputModify dm(i7+0)=ax0; // store input into delayline. //************ FIR loop. tapsDivRatio taps filter cntr=my1; // cntr = taps-1; mr=0, mx0=dm(i1,m1), my0=pm(i7,m7); // mx0=*(h++), my0=*(delayline++) do firLoop until ce; // Let's Roll! firLoop: mr=mr+mx0*my0(ss), mx0=dm(i1,m1), my0=pm(i7,m7); // mx0=*(h++), my0=*(delayline++) mr=mr+mx0*my0(rnd); // last mac. sat mr; // round and sturate it. outerloop: dm(i6+=m6)=mr1; // *(output++)=result //******************** finalization of pointer // put back curPtr* get_arg( i0, curPtr ); dm(i0+=m0)=i7; // curPtr* = current delayline pointer. i0 is destroyed //******************** Stack frame l7=0; // restore l7 l1=0; // restore l1 pops(mstat); // restore mstat and_pops(m0); // restore m0 and_pops(i7); // restore i7 exit; // dispose frame and say good bye
次は⇒ラッパークラス