2008年08月30日 [ColdFire] ColdFire×P2P地震情報(3) サンプリングを等間隔に行おう
_ [ColdFire] ColdFire×P2P地震情報(3) サンプリングを等間隔に行おう
Interface 9月号付録基板を使い,P2P地震情報のサービスを作ってしまう勝手な企画です.過去の記事は,タイトルのColdFireカテゴリからどうぞ.
前回のおさらい
- 加速度センサの値0〜4095と,加速度-2022.62 Gal〜+2022.62 Gal(重力加速度-2.06G〜+2.06G)が対応しています.
- 地震計として使うためには,「ズレ」「ノイズ」「サンプリング間隔」の3点を考える必要があるようです.
番外編: アナログとデジタル
アナログデータをデジタルデータに変換する時に問題となるのは,アナログデータは常に変化していて,その変化は連続的であるという点です.一方のデジタルデータは,変化の時間間隔と変化の単位が定められており,離散的です.
時計で言うと,連続秒針タイプのアナログ時計はズズズーッと滑らかに時を刻みますが,デジタル時計は秒単位で刻むことしか出来ません.アナログのモノをデジタルに変換する時には,どうしても情報の欠落が生じます.
サンプリングってなんだっけ?
今回は,課題のうち「サンプリング間隔」を取り上げます.サンプリングは,アナログデータをデジタルデータに変換する時に必要となるものです.どんなものかをざっくり見ておきます.
音声,電圧などのアナログデータ(波形)がある時に,一定の時間間隔でデータを読み取ることを『標本化(サンプリング)』と言います.1秒あたりの読み取り回数をサンプリング周波数(Hz)で表します.
それだけです.サンプリングは一定間隔で行うものですから,サンプリングを等間隔で行うのは当然なわけです.
ただし,デジタルデータに変換するには『量子化』も必要です.その時のデータがどの程度なのかを離散的な(とびとびの)数値で表します.その細かさを量子化ビット数で表します.8bitだと256段階の値で表すことが出来るわけです.
等間隔に行うということは,時間の概念を使うということ
サンプリングを等間隔に行うということは,一定の時間間隔で行うということであって,すなわち時間の概念を取り入れなければなりません.仮にサンプリング周波数が10Hzであるならば,1秒に10回,100ミリ秒に1回の計測を行う必要があるわけです.
SilentCのタイマ
というわけで,時間の概念を取り入れましょう.SilentCの仕様を見ると,タイマが用意されています(SilentSystemの(PDF) OS-1プログラミングマニュアルを参照).マニュアルではタイマーと表記されていますが,ここではタイマと表します.
SilentCのタイマでは,作成,更新(上書き),取得,削除の4つが可能です.カウントが0になった時に関数を呼べるような記述がありますが,うまく行かなかったのでパスします.
まず思いつくのは,サンプリング間隔(10Hzであれば100ms)のタイマを作って,カウントが0の時にデータの取得・タイマの上書き動作を行う方法です.
main(){ char t1; int cnt; t1=CreateTimer(0,10,0);cnt=0; for(;;){ if(GetTimerCount(t1)==0){ CreateTimer(t1,10,0);cnt++; if(cnt%5==0){PrNum(cnt);PrStr("\r\n");} if(cnt==600)break; } SystemSleep(); } PrStr("Complete.\r\n"); CreateTimer(t1,0,0); }
100 msのタイマを600回,つまり60 sのあいだ実行します.WireSharkでパケットを見ると,実行時間は60.01 sとなかなか正確です.しかし,これでは問題があります.
- ループ内に様々な処理を加えると,「タイマカウンタが0になってから,0かどうか調べて更新するまでのディレイ」が起こり,それが蓄積する可能性があります.
- 1ループに掛かる処理時間がタイマの指定時間を上回っていれば,確実にズレます.
これらの問題を緩和しましょう.次のようなプログラムを用意します.引数は hz: サンプリング周波数,slp: スリープ時間(10 ms単位) です.ただし,hzは100の約数を指定します.
ctr(int hz, int slp){ char t1; int cr,nx; int i; i=0; hz=100/hz; nx=30000; t1=CreateTimer(0,30000,0); #stop 0 for(;;) { if(Getc(0)!=0)break; cr=GetTimerCount(t1); while(cr<=nx){ i++;PrNum(i);PrStr("\r\n"); nx-=hz; if(nx<0)nx+=30000; } if(cr==0)CreateTimer(t1,30000,0); if(slp==0){SystemSleep();}else{Sleep(slp);} } CreateTimer(t1,0,0); }
- タイマカウンタを30000(300 s)に設定することで,「タイマカウンタが0になってから,0かどうか調べて更新するまでのディレイ」の発生頻度を300 s/回に抑えています.
- その代わり,実行判定を「タイマカウンタが0かどうか」でなく「タイマカウンタが次の実行時間を過ぎているか」としています.
- 1回のタイマカウンタ取得で,複数回の実行を認めています.
- "タイマカウンタ取得間隔 > サンプリング間隔" となった場合にサンプリング回数を稼ぎ,「全体で見ればサンプリング周波数を保っている状態」に立て直します.
FILENAME::ctr(10,123) と呼び出してみると,サンプリング間隔こそ狂えどサンプリング回数は10 回/秒になっていることが分かります.
あまり良い方法ではないのは確かですが,この辺りで妥協します.
今回のまとめ
- タイマを使うことで,サンプリング回数を保つことがそれなりにできました.