トップ «前の日記(2008年08月27日) 最新 次の日記(2008年08月31日)» 編集

のろのろのろ雑記


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点を考える必要があるようです.

番外編: アナログとデジタル

 アナログデータをデジタルデータに変換する時に問題となるのは,アナログデータは常に変化していて,その変化は連続的であるという点です.一方のデジタルデータは,変化の時間間隔と変化の単位が定められており,離散的です.

 時計で言うと,連続秒針タイプのアナログ時計はズズズーッと滑らかに時を刻みますが,デジタル時計は秒単位で刻むことしか出来ません.アナログのモノをデジタルに変換する時には,どうしても情報の欠落が生じます.

サンプリングってなんだっけ?

 今回は,課題のうち「サンプリング間隔」を取り上げます.サンプリングは,アナログデータをデジタルデータに変換する時に必要となるものです.どんなものかをざっくり見ておきます.

080828_sampling.png

 音声,電圧などのアナログデータ(波形)がある時に,一定の時間間隔でデータを読み取ることを『標本化(サンプリング)』と言います.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となかなか正確です.しかし,これでは問題があります.

  1. ループ内に様々な処理を加えると,「タイマカウンタが0になってから,0かどうか調べて更新するまでのディレイ」が起こり,それが蓄積する可能性があります.
  2. 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 回/秒になっていることが分かります.

 あまり良い方法ではないのは確かですが,この辺りで妥協します.

今回のまとめ

  • タイマを使うことで,サンプリング回数を保つことがそれなりにできました.

参考