プログラムでCICフィルタ
by K.I
2017/01/01〜2017/01/16
Index
- 微分回路と積分回路をカスケード接続したSINCフィルタは、遅延と加算器だけで作ることが出来る
- SINCフィルタを、さらにカスケード接続して微分器と積分器の順番を変えて纏めたものを
- CICフィルタ(Cascaded Integrator Comb filters)という1
- デジタル信号処理も、最近やっと、いろいろ分かってきたような気がするので、
- 以前から面白いと思っていたCICフィルタについて、プログラムで確認しながら自分なりに纏めてみた
- まぁかなり前になるが、CICフィルタの動作を確認するために、 fftwでいろいろ実験の
- 元になるプログラムを作ったのが、本当の最初のきっかけだったんだけど。。
- CICフィルタの特性は、周波数等間隔の櫛型になるので、、
- この記事の周波数特性は、全部、周波数軸がリニア表示になっている。
- CICフィルタを、Unixのフィルタプログラムとして作成、コマンドライン上で組み合わせてデータ処理させる。
- プログラムの変数は、基本的にintにしてるので、自分の環境2では32bitのバッファになる
- プログラムでデジタルフィルタの記事でも、SINCフィルタのプログラムを作ったが、それは実数計算だった。
- 今度は、FPGA等で実際に作ることを考慮して、積分・微分回路を整数値で計算して、CICフィルタのプログラムを作ってみる
- プログラムは、基本的には fftwでいろいろ実験で用意したものを使っている。
- 整数化に使ってる imul と、整数出力のDDS idds は、 デルタシグマ変調器の記事で作ったもの
- 整数入出力の積分・微分フィルタをこの記事の最初に、アップサンプラ、ダウンサンプラをおまけで作成3している。
1というより、SINCフィルタも含めて、微分器・積分器のカスケード接続によるフィルタをCICフィルタと呼んでも良いと思う。
2これは環境によって異なるので、最初にintの大きさを確認しておく。
3まぁ、入出力をintにしただけなんだけど。
[top]
- 積分フィルタと微分フィルタをカスケード接続したものが、SINCフィルタ
- データを全て加算していけば積分になる
- 1つ前のデータを保持しておいて、新しいデータと加算する


#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int line[256];
int main(int argc, char *argv[])
{
int i, m=1;
int in, out;
if (argc>1)
m = atoi(argv[1]);
for (i=0; i<m ;i++) line[i]=0;
i=0;
while (scanf("%d", &in) != EOF) {
out = line[i];
line[i] += in;
i = (i+1)%m;
printf("%d\n",out);
}
return 0;
}
- 積分回路の特性4 5
- 擬似ホワイトノイズを入力して、周波数特性をみてみよう
% white 10000 | imul 100 | intg | fftw | gp_fftl > wtintg.png
- 微分を、ある時間のデータの変化量を求めることと考えて、


#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int line[256];
int main(int argc, char *argv[])
{
int i, m=16;
int in, out;
if (argc>1)
m = atoi(argv[1]);
for (i=0; i<m ;i++) line[i]=0;
i=0;
while (scanf("%d", &in) != EOF) {
out = in-line[i];
line[i] = in;
i = (i+1)%m;
printf("%d\n",out);
}
return 0;
}
- fs/Mの周期でゲインが0に近づく櫛型の特性を持つフィルタになる
- 微分フィルタと積分フィルタをカスケード接続すると、SINCフィルタになる






% white 10000 | imul 100 | comb | intg | fftw | gp_fftl > wtci.png
% white 10000 | imul 100 | intg | comb | fftw | gp_fftl > wtic.png
- fs/Mの周期でゲインが0に近づく櫛型のフィルタだが、DCではゲインがMで緩やかなローパス特性になる










% white 10000 | imul 100 | comb | intg | comb | intg | fftw | gp_fftl > wtcici.png
% white 10000 | imul 100 | intg | comb | intg | comb | fftw | gp_fftl > wticic.png














% white 10000 | imul 100 | comb | intg | comb | intg | comb | intg | fftw | gp_fftl > wtcicici.png
% white 10000 | imul 100 | intg | comb | intg | comb | intg | comb | fftw | gp_fftl > wticicic.png
- CICフィルタは順番を変えて、微分・積分回路を纏めても、特性は同じことを確認する














% white 10000 | imul 100 | comb | comb | comb | intg | intg | intg | fftw | gp_fftl > wtccciii.png
% white 10000 | imul 100 | intg | intg | intg | comb | comb | comb | fftw | gp_fftl > wtiiiccc.png
- 微分→積分と纏めても、積分→微分と纏めても、全く同じ特性
- CICフィルタでは順番を変えても、周波数特性は同じになる6ことが分かる
- 以降は、いろいろな組み合わせを作るのも面倒なので、適当な順番のみ試すことにする。
- SINCフィルタ(CICフィルタ)は、基本的にLPFだが、
- LPFとしては、それほど急峻な特性ではない
- また、特性が平坦ではなく、櫛状になっている
- でも、乗算器を使わず加算器のみの単純な構成になるので、実際の回路では作りやすい
4サンプリング周波数を10kHzにしているのは、ある程度大きくしないと荒すぎるし、あまり大きいと時間が掛かりすぎるので。
5整数化時にx100してるのも同様の理由だが、有効な入力ビット数に限界もあるので適当に決めた。
6入力データに対して、十分な大きさのバッファで処理した場合は。
[top]


→ R=16 とする
- サンプリング周波数24kHzで1kHzの正弦波を、x16のアップサンプリングしてみる
- 元の波形とアップサンプリングしたものにfftwを掛けて、周波数特性を見てみる
- 0〜24kHzの範囲が繰り返された周波数特性になっている。
- 1kHz以外に 折り返しで、23kHz,25kHz,47kHz,49kHz…と、余計な周波数が出来ちゃったことが分かる
- アップサンプリング前のサンプリング周波数をfsとすると、
- アップサンプリングによって、0〜fsの周波数成分が、fs〜2fs,2fs〜3fs…というようにコピーされるイメージ
- 必要なのは、0〜fs/2の部分だけ7なので、
- それ以外の周波数成分は、全て余計なもので、これがあるために元の信号波形とは異なってしまっている。
- サンプリング定理では、元の波形に含まれる最大周波数成分の2倍以上の周波数でサンプリングすれば、
- 0挿入によるアップサンプリング処理は、インターポーレータ或いはインターポーレーションフィルタとも言う
- インターポーレータには、普通、生成された余計な周波数成分を除くためのLPFが必要となる
- SINCフィルタは櫛型の特性を持つので、アップサンプリングの倍率と同じ段数のSINCフィルタを通せば、
- アップサンプリングで生じた余計な周波数をうまく抑制してくれるはず




- M=16のSINCフィルタの特性8と、アップサンプラ+SINCフィルタ後の周波数特性
% white 3840 | imul 100 | comb | intg | fftw | gp_fftl > wci.png
% idds 1000 24000 | iups | comb | intg | fftw | gp_fftl > uci.png
- フィルタを通すことで、実際の波形も補間されていることが分かる
% idds 1000 24000 | iups | comb | intg | head -400| gp_wav > uciw.png
- さらにもう一段、2段のSINCフィルタを通してみる






- SINC2段のフィルタ特性と、アップサンプラ+SINC2段フィルタ後の周波数特性
% white 3840 | imul 100 | comb | comb | intg | intg | fftw | gp_fftl > wccii.png
% idds 1000 24000 | iups | comb | comb | intg | intg | fftw | gp_fftl > uccii.png
- 実際の波形も多少カクカクしているが、正弦波に見えるようになった。
% idds 1000 24000 | iups | comb | comb | intg | intg | head -400| gp_wav > ucciiw.png
- さらにもう一段、3段のSINCフィルタを通してみる








- SINC3段のフィルタ特性と、アップサンプラ+SINC3段フィルタ後の周波数特性
% white 3840 | imul 100 | comb | comb | comb | intg | intg | intg | fftw | gp_fftl > wccciii.png
% idds 1000 24000 | iups | comb | comb | comb | intg | intg | intg | fftw | gp_fftl > uccciii.png
- 実際の波形も、補間されて、かなりなめらかな正弦波になっている
% idds 1000 24000 | iups | comb | comb | comb | intg | intg | intg | head -400| gp_wav > uccciiiw.png
- 前項の回路では、微分回路はxMアップサンプル後のデータから、M間隔でデータを取り出しているので、
- 微分回路をM=1にして、×Mアップサンプリングの前に持ってきても、同じ動作になるはず








% idds 1000 24000 | comb 1 | comb 1 | comb 1 | iups | intg | intg | intg | fftw | gp_fftl > cccuiii.png
- この構成にすると、微分回路の遅延回路の数を節約できる。
- この例の場合は、16×3=48個必要だったものが、1×3=3個で済む9ことになる
- これを実際の回路に組む場合は、遅延回路はFFで作るので
- 微分回路のクロックを24kHzにして、積分回路のクロックを384kHzにするだけでよいことになる
- アップサンプラは零挿入するだけの簡単なもので良い
- これは、CICフィルタを使ったインターポーレータとして良く使われる構成らしい。
7折り返しのイメージMfs-fs/2〜Mfsも含むけれども。
8384ks/sのフィルタ特性を求める時は、擬似ホワイトノイズ生成に時間が掛かる(というよりプログラムの限界がある)ので、1/100のサンプリング周波数で計算している。
9実際は、Bit幅分のレジスタが必要なので節約の効果は大きい。
[top]


→ R=16 とする
- サンプリング周波数384kHzで1kHzの正弦波を、x1/16のダウンサンプリングしてみる
- 元の波形とダウンサンプリングしたものにfftwを掛けて、周波数特性を見てみる
- うまくダウンサンプリングされている
- でも、これじゃ全然問題点も分からないので、周波数範囲を持ったデータで考えることにする
- サンプリング周波数384kHzで、1/16ダウンサンプリングすると、
- ダウンサンプリング後のサンプリング周波数fsは、24kHz
- サンプリング定理から、fs/2の12kHz以下の周波数しかサンプリングできない
- fs/2以上の周波数が含まれる場合は、雑音になってしまう
- ダウンサンプリング後のサンプリング周波数fs/2以下のデータのみの場合11
- ダウンサンプリング後のサンプリング周波数fs/2以上のデータを含む場合
- この場合、12kHz以上のデータが、折り返しで重なってしまっている
- ダウンサンプリング後のサンプリング周波数をfsとすると、
- ダウンサンプリングによって、fs以上の周波数成分が、折り重なって加算される
- 必要なのは、0〜fs/2の部分だけ12なので、ダウンサンプリング前のデータが、
- それ以外の周波数成分を含む場合、全部エイリアスになってしまう
- アナログデータから、D/A変換でサンプリングする際も、LPFで高い周波数成分をカットして、
- 最大の周波数成分の2倍以上で、サンプリングする必要があるのと全く同じ理由
- データの間引きによるダウンサンプリング処理を、デシメータ或いはデシメーションフィルタとも言う
- 元データがfs/2以上の周波数成分を含む場合、デシメータの前にエイリアスになる周波数成分を除くためのLPFが必要となる
- fs/2以上のデータは邪魔なので、ダウンサンプラ前にカットする必要がある
- ダウンサンプラの倍率と同じ段数のSINCフィルタは、エイリアスになる周波数をうまくカットしてくれそう
- なので、3段のSINCフィルタを通してからダウンサンプリングしてみる








- SINC3段のフィルタ特性と、SINC3段+ダウンサンプラの周波数特性
% white 3840 | imul 100 | intg | intg | intg | comb | comb | comb | fftw | gp_fftl > wiiiccc.png
% white 3840 150 | imul 100 | intg | intg | intg | comb | comb | comb | idws | fftw | gp_fftl > wiiicccd.png
- SINC3フィルタは、0〜fs/2の部分以外をうまく抑制してくれている。fsの倍数の周波数を特に抑制してくれるのが良い。
- エイリアスとなる周波数がある程度、抑えられていることがわかる
- 前項の回路では、微分回路はM間隔でデータを取り出して処理するので、
- 微分回路をM=1にして、×1/Mダウンサンプリングの後に処理しても、同じ動作になるはず








% white 3840 150 | imul 100 | intg | intg | intg | idws | comb 1 | comb 1 | comb 1 | fftw | gp_fftl > iiidccc.png
- この構成にすると、インターポーレータの時と同様に、微分回路の遅延回路の数を節約できる。
- この例の場合は、16×3=48個必要だったものが、1×3=3個で済む13ことになる
- これを実際の回路に組む場合は、遅延回路はFFで作るのでダウンサンプラは必要ない。
- 積分回路のクロックを384kHzにして、微分回路のクロックを24kHzにするだけでよいことになる
- これは、CICフィルタを使ったデシメータとして、良く使われる構成らしい。
- 予め、帯域制限していない擬似ホワイトノイズを、そのままダウンサンプリングしたものと、
- そのままダウンサンプリングすると、エイリアシングによって周波数特性が暴れているが、
- CICフィルタ付きの場合は、帯域制限したものと同じような特性になっており、フィルタの効果があるようだ
10いや、やっぱり波形見ただけじゃ分からないか。。
11やはり384ks/sのフィルタ特性を求める時は、擬似ホワイトノイズ生成に時間が掛かるので、1/100のサンプリング周波数で計算しています。
12どうしても折り返しのイメージMfs-fs/2〜Mfsも含むけれども。
13Bit幅分のレジスタが必要なので節約の効果はやっぱり大きい。
[top]
- CICフィルタは単純な構成なのに、いろいろ興味深い。
- 補間処理がフィルタを通すだけというのは、理屈が分かっても、やっぱり不思議
- SINCフィルタと、サンプリングの処理の相性が良いのは
- 最初は、たまたまと思っていたんだけど、実はとても関連性が深そうというのが
- 最近、やっと分かってきたような気がする
- サンプルレートの変更も、単純な0挿入や、データの読み飛ばしだけなのに、
- フィルタで基本のプログラムを作っておいて、いろいろ組み合わせることで、
- 思いついた事をすぐ試して確認できるのは、なかなか楽しい。
- CICフィルタだけだと、LPFとしてはちょっと甘いし、高域が落ちてしまう欠点もある
- 実用的にはいろいろ問題があるらしいが、基本の回路としてはとても分かり易いと思う。
- CICフィルタでサンプルレート変換という記事で、ここで作ったCICフィルタのプログラムを使って、
- サンプルレートコンバータの実験をしてみました。そちらも参照してみてください。(170126追記)
[top]
- プログラムは整数化してるだけなので、載せる意味もないんだけど、
- アップサンプラの入出力をintにしたもの(iups.c)
#include <stdio.h>
#define L (16)
int main(int argc, char *argv[])
{
int i, l;
int in;
l = L;
if (argc>1)
l = atoi(argv[1]);
while (scanf("%d", &in) != EOF) {
for (i=0; i<l ;i++)
if (!i)
printf("%d\n",in);
else
printf("%d\n",0);
}
return 0;
}
- デフォルトでは、16回に1個だけデータを出力して、あとは0を出力する
- ダウンサンプラの入出力をintにしたもの(idws.c)
#include <stdio.h>
#define M (16)
int main(int argc, char *argv[])
{
int i=0, m;
int in;
m = M;
if (argc>1)
m = atoi(argv[1]);
while (scanf("%d", &in) != EOF) {
if (!(i++%m)) printf("%d\n",in);
}
return 0;
}
- デフォルトでは、16個毎に1個だけデータを出力して、あとは捨てる
- 積分回路は、DC成分があれば簡単にオーバーフローしてしまうはずだが、
- 2の補数演算の場合は、有効なビット幅があれば、オーバーフロー分は捨ててしまっても問題ないらしい
- CIC_Filter_Introductionによると、
- CICデシメータの最終コムフィルタのゲインGは
^N&chf=bg,s,ffffff00)
- 2の補数演算の場合、出力のビット幅は

- CICインターポーレータの、ステージiのゲインGiは
^{i-N}}{R} %26 i = N%2B1, \cdots ,2N \\ \end{cases}&chf=bg,s,ffffff00)
- ステージiのビット幅Wiは

- そして

- って書いてあるけど、自分の読解力・数学力の無さで、いまいち良くわからない。
- 少なくともCICデシメータの場合、ゲインは
で、必要なBit幅は、入力Bit幅 + N log2 M のようだ
- インターポーレータは、先に微分器が来るので、同程度のBit幅があれば良さそうな気がする14
- デジタル・デザイン・ノートの CICフィルタのページ
- この記事を書いてるとき見つけたが、とても分かり易く、他にもいろいろ参考になりそうな記事が一杯ある
14でもまぁ、あまり自信なし。。
[top]
[プログラムの部屋に戻る]
⇒ Disqusの広告がうるさすぎるので基本は非表示にしました