dsPIC33FJ64GP802 (7) --- DSP 命令と DSP ライブラリ (5)

 DSP ライブラリのドキュメントでは、IIR (Infinite Impulse Response) フィルタ係数は「dsPIC Filter Design」(略称: dsPICFD) プログラム (有償) によって生成されたもの (あるいはそれに等価なもの) を使うように指示されていて、係数値の取るべき詳細な条件については明記されていません。
 IIR フィルタの構成については、色々なバリエーションがあり、その定義によっては符号が反対になったり、設計プログラムで求まった数値をスケーリングする必要があったりします。
 一方、FIR (Finite Impulse Response) フィルタのタップ係数はフィルタのインパルス応答そのものであり、意味が自明なので、他のフィルタ設計プログラムの出力の流用も容易です。
 dsPICFD については、フル機能版が 300 ドル程度、機能限定版の dsPICFD Lite は 30 ドル程度で販売されていたようですが、現在は取り扱いがないようで、Microchip の web サイト上で検索しても見つかりません。
 DSP ライブラリ中の (普通の) IIR フィルタ関連の構造体/関数のプロトタイプは次のようになります。
 まず、「標準型」(直接型 II) の構成の IIR フィルタについては、

typedef struct{
  int         numSectionsLess1;
  fractional* coeffsBase;
  int         coeffsPage;
  fractional* delayBase;
  int         initialGain;
  int         finalShift;
} IIRCanonicStruct;
 
void IIRCanonicInit(
         IIRCanonicStruct* filter 
);

fractional* IIRCanonic(
                int         numSamps,
                fractional* dstSamps,
                fractional* srcSamps,
                IIRCanonicStruct* filter
);

 転置型 (直接型 II) の IIR フィルタについては、

typedef struct{
  int         numSectionsLess1;
  fractional* coeffsBase;
  int         coeffsPage;
  fractional* delayBase1;
  fractional* delayBase2;
  int         finalShift;
} IIRTransposedStruct;
 
void IIRTransposedInit(
         IIRTransposedStruct* filter
);

fractional* IIRTransposed(
                int         numSamps,
                fractional* dstSamps,
                fractional* srcSamps,
                IIRTransposedStruct* filter
);

となっています。
 FIR フィルタでは「FIRStruct」は複数のタイプで共通の構造体でしたが、IIR では「Canonic」と「Transposed」それぞれ「専用」の構造体を使用しています。
 そのためか、それぞれの IIRStruct を初期化する関数は用意されていません。 したがって、ユーザが構造体の各フィールドを明示的に設定する必要があります。
 上記の IIR~Init() 関数は、フィルタ内部の遅延要素をゼロ・クリアするための関数で、構造体の初期化のためのものではありません。 FIR では「FIRDelayInit()」という名前でしたが、 IIR では「Delay」が省略されています。
 まず、「IIRCanonic()」での処理の様子と伝達関数の表現との対応を、2 次セクション 1 段の場合について示します。

 「標準型」では、伝達関数の分母の多項式を先に計算し、次に分子の多項式を計算します。
 一般に、フィルタの Q が大きいと、分母のピーク・ゲインが大きくなるので、「クリップ」が生じる可能性が高くなります。 クリップを防ぐため、あらかじめ信号入力をプリ・スケーリングしてレベルを下げておくために IIRCanonicStruct 構造体に「initialGain」フィールドと、「finalShift」フィールドが用意されています。
 「プリ・スケーリング」なしの場合、initialGain は「Q15 の表現で 0.5」 (0x4000) を設定します。 プリ・スケーリングしてレベルを下げる場合は、initalGain を 0.5 以下の値にします。
 「finalShift」は「ポスト・スケーリング」のためで、アキュムレータのビット・シフトにより実現しているので、1/4 倍、 1/2 倍、1 倍、2 倍、4 倍、8 倍 ... といった値しか取れません。
finalShift の値は、ポスト・スケーリングなし (1 倍) の場合は「0」、左シフトの場合はシフトするビット数の符号をマイナスにしたもの (2 倍なら -1、4 倍なら -2) を指定します。
 DSP ライブラリでは、伝達関数の分母多項式の係数を「an」、分子多項式の係数を「bn」と呼んでいます。
 また、伝達関数の式の分母多項式の係数 a1、a2 は、フィルタ計算で使う係数と符号が反対になります。
 IIR フィルタが安定である条件として、「極」が z 平面の単位円の内側に存在することが要求されます。
 フィルタの Q が高い場合、極の位置は単位円に近づき、伝達関数の分母多項式の 2 次の係数 a2 の絶対値は「1」よりは小さいが「1」に近い数となり、1 次の係数は -2 以上、+2 以下の数となります。
 dsPIC の MAC 命令では、フォーマット 1.15 (Q15) の数と 1.15 (Q15) の数の積和を求め、結果を 1.15 (Q15) で返す仕様になっています。
 「1.15」(Q15) では絶対値 1 を超える数は表現できないので、1 次の係数が表現できないことになってしまいます。
 そこで、IIR 2 次セクションひとつ当たり 5 個ある係数をすべて 1/2 の値として、1.15 (Q15) で表現できるようにして、あとは計算のつじつまが合うように考慮します。
 1/2 して Q15 で表現するということは、2.14 (Q14) フォーマットで元の数を表現するのと等価になります。
 この方針での dsPIC の MAC 命令と SAC.R 命令 (丸め付きのストア・アキュムレータ命令) の処理の様子を図で示します。

 「Wy」を遅延要素の値を読み出したワーキング・レジスタとします。 これは標準の 1.15 (Q15) フォーマットになっています。
 一方、「Wx」をフィルタ係数を読み出したワーキング・レジスタとします。 これは 2.14 (Q14) での表現となっています。
 Wx と Wy とを掛け合わせると、結果は 2.30 (Q30) の数となります。 標準では 1.31 (Q31) になるはずですから、本来の数値の 1/2 になっています。
 これをアキュムレートしていくので、40 ビット・アキュムレータ内には 10.30 (Q30) のフォーマットで数値が保持されます。 本来は 9.31 (Q31) のフォーマットです。
 MAC 命令の「積和演算」の段階では「補正」はできず、SAC.R 命令のシフト機能を利用して、外部の遅延要素の 16 ビット・メモリに書き出す際に丸めを施した後に 2 倍して正しい値を回復します。 このとき、結果が 1.15 (Q15) に収まらないでオーバーフローした場合には、飽和演算機能により適切な値にクリッピングされます。
 次回は、IIRCanonic() で 2 次セクションを 2 段以上にした場合と、IIRTransposed() について説明します。