さて、インターポレータを実装したら次はデシメーターです。せっかくですのでポリフェーズ フィルタを使っていきたいところですが、その前によく考えてみる必要があります。
ポリフェーズフィルタを使ったデシメーションは確かに魔術的なアルゴリズムです。直感的に正しくないように見えますがきちんと動くところは、「こいつをどう実装してやろう」と心ときめかないこともありません。が、よく考えてみるとこの方法は今回はそれほど魅力的ではありません。
インターポレータの実装と同じく、デシメーターも入力バッファに複数サンプルを蓄積してそれをデシメーションするよう実装します。この場合、蓄積が前提なのでダウンサンプル比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
次は⇒ラッパークラス