Windows Phone FM Radioの電波強度測定 - 数値計算は誤差が憑き物だよね

私にとって実世界の情報を取り込むものは全てセンサーだ…って訳でWindows Phone 7のFM Radioの電波強度測定を試してみた。

Windows Phone 7のFM Radioをアプリから操作するのは凄く簡単。Microsoft.Devices.Radio名前空間のFMRadioクラスを使います。
このクラスはシングルトンで、インスタンスは、

FMRadio.Instance

で取り出せます。
そして、このクラスのFrequencyというプロパティが周波数を、SignalStrengthというプロパティが電波強度を意味しています。
※MSDNのSignalStrengthプロパティの説明には、”Retrieves the received signal strength indicator (RSSI) value for the currently tuned frequency.”とあるので、Frequencyに指定した周波数の強度であることは間違いない模様
なので、

FMRadio.Instance.PowerMode = RadioPowerMode.On; // ラジオのスイッチをオン
FMRadio.Instance.Frequency = 81.3
var strength = FMRadio.Instance.SignalStrength;

と書けば、直ぐ特定の周波数の電波強度を測れるわけです。あ、イヤホンのコードがアンテナなので、レィディオを使う場合にはイヤフォンをぶっさしておくことを忘れずに。

色々試してみた結果を以下に列記しますね。

Frequencyプロパティに設定できる値の範囲:
Frequencyに代入できる値は、FMRadioのCurrentRegionプロパティに設定されている値で変わります。CurrentRegionにはRagioRegion列挙子で定義された、Europe、UnitedStates、Japanの三つを指定できます。日本では当然Japanであり、指定できるのは76.0~90.0の範囲です。この範囲より小さかったり、大きかったりする値を代入しようとすると、例外が発生するのでご注意。私がアプリを作った時には、何故か?CurrentRegionがUnitedStatesになっていたので、アプリの初期化時に確認してJapanを代入するコードを入れておくと良いでしょう。

Frequencyプロパティに設定できる値の制限:
更に、Frequencyには、小数点第2位以下が0の値しか代入できません。これも中途半端な値を入れると例外が発生してしまうのでご注意。なので、Sliderで周波数を指定しようとすると一発で嵌ります。Sliderを使う場合には、0.1単位で丸める処理を入れないと正しく動きません。
これに関連して、例えば以下のようなコード、

double freq = 76.0;
double delta = 0.1;
while (freq <= 90.0)
{
    ....
    freq += delta;
}

と書いて順次周波数を変えて、全周波数を舐めよう…なんてコードを書くと上手く動きません。なぜなら、doubleというデータ型には誤差があるからです。.NETのdoubleはIEEE754フォーマットを採用しています。浮動少数の有効桁が15桁~16桁しかなく、単純に足し算すると誤差が出てしまいます。ためしにWPFアプリで、以下のコードを書いて試してみてください。

            double x = 1.0;
            double d = 0.1;
            double y = x;
            int loop = 1000;
            for (int i = 0; i < loop; i++)
            {
                // Ceiling関数を使って厳密に0.1ずつyの値が増えていくように計算
                y = (double)((double)((int)System.Math.Ceiling(y * 10.0) + (int)System.Math.Ceiling(d * 10.0)) / 10.0);
                // 単純計算
                x += d;
                if (x != y)
                {
                    MessageBox.Show("Miss!");
                    break;
                }
            }

かなり早い段階で、xとyの値が異なり、単純足し算では正確な値が出ないことが判るでしょう。
コンピュータ上の数値計算は、.NETに限らず、VC++以外のC++やその他の言語(FORTRAN含む)でも必ず発生する問題なので、安易な数値計算式で済ませるのではなく、必要とされる精度はいかほど?と使用するデータ型の精度はいかほど?の二点を、いつも考慮して要件を満たすプログラムを書くことを心がけましょう。
でないと、あなたの書いた制御コードで人類の滅亡を招いてしまうかもしれません。(ま、大げさに言えばですが)

さて、話を元にもどしてFMRadioのFrequencyの場合は、0.1の精度が必要なので、

double freq=76.0;
while (freq <=90)
{
    …
    freq = (double)((int)System.Math.Ceiling(freq * 10.0) + 1.0) / 10.0;
}

的なコードを書けば、0.1未満の誤差が出ません。めでたしめでたし。

尚、FMRadioを使ったアプリをMarketplaceに申請中(2011/12/2現在)です。名前は、”FM Radio Wave Level Checker”(長い!!)。

スライダーで周波数を指定して、指定した周波数の電波強度を0.1秒単位でグラフ表示したり、76.0~90.0の全周波数の強度を0.1秒単位でスキャンしグラフ表示したり、端末が向いている方向(アンテナとの相関は今一なのはご愛嬌)と周波数の電波強度を表示します。昔の(今もあるか?)電子回路工作本には、FM電波を使って音声を飛ばす自作トランシーバーの回路図が掲載されていて、自作が可能でしたが、秋葉原に行ってトランジスタや抵抗を買い込んで半田付けして電波発信機を作ってこのアプリと組合せれば、スパイおもちゃの出来上がり、てぇ寸法。
※自作で作る電波発信機の周波数帯、電波強度が強すぎたりすると電波法に触れてお縄になるので本当に試す場合(いないよねぇそんな人)は、十分にご注意くださいませ