ビーコンの距離測定精度を上げるためのキャリブレーションを行うアプリをDelphiで作る

これは [Delphi Advent Calendar 2016](http://qiita.com/advent-calendar/2016/delphi) 10日目の記事です。

また、前回の記事 [DelphiでスマホやPCをBLEビーコンにしたり、ビーコンの電波を受けてみたりする](http://qiita.com/kazinoue/items/c16189508f20be5a9bea) の続きです。

>※2018年7月11日 追記
この内容は記事作成時点の2016年にリリースされた Delphi/C++Builder/RAD Studio 10.1 Berlin Update 2 で動作を確認して作成しています。しかし 10.2 Tokyo で Windows向けにビルドすると RSSIが計測できません（iOS, Android では計測可能です）。本件については [Quality Portal RSP-20214 (閲覧にはEDNアカウントでのログインが必要)](https://quality.embarcadero.com/browse/RSP-20214?focusedCommentId=52302) も併せてご参照ください。

# ビーコン（iBeacon)で距離測定の基本的な話
iBeaconは、BluetoothLEとのアドバタイズを使ったビーコンです。そしてBluetoothLEというのは2.4GHzの周波数帯(ISMバンド)の電波を用いる規格です。

さて、ビーコンで距離を測るというのはどういうことでしょうか。基本的にはこういうことです。

- 電波の強さは距離の２乗に反比例して弱くなる。
- 「ビーコンから1メートル離れた場所で測定した電波の強さ」が分かっていれば、別の地点で測定した電波の強さとの比較や計算により、その場所がビーコンからどれくらい離れているかを知るころができる。

だからビーコンの電波の強さを1メートル地点で測った実測値と、現在の場所で受信した実測値が分かっていれば、ビーコンからどれくらい離れているかを計算できます。しかしすべてのビーコンの1メートル地点の電波の強さをアプリケーション側でリスト管理するのは管理が面倒ですよね。

そこでiBeaconでは「ビーコン自身が1メートル地点の電波の強さを通知する」ことが仕様の中に含まれています。**この電波の強さは TxPower として通知されます。**

しかし、実際のビーコンの TxPower を測定してみると、1メートル地点の電波強度とは必ずしも合致しない場合があります。また、アプリ開発のフレームワークにはPCやスマホをビーコンにするコンポーネントを含む場合がありますが、それらのデフォルトのTxPowerをそのまま用いてみると、実測した電波強度とは異なることがあります。

電波の強さは障害物や使用環境で変化しますが、そもそもキャリブレーションが行われていないのでは、それは環境以前の問題です。そこでビーコンによる距離の判定精度を下げないために、ビーコンのTxPowerをキャリブレーションすることにします。

# iBeaconのキャリブレーション方法
iBeaconのキャリブレーションは、基本的には次のような手順で行います。

- iBeaconから1メートル離れた地点に測定用の機器を置く。
- iBeaconから1メートルの同心円の接線方向に対して30cmの範囲で測定用機器を動かして電波強度(RSSI)を計測する。
- RSSIの平均値を TxPower として用いる。

この内容はApple の開発者向けサイトにて以下のURLで公開されている文書で解説されています。

https://developer.apple.com/ibeacon/Getting-Started-with-iBeacon.pdf

このことから、ビーコンのキャリブレーションを行うために以下の処理が必要です。

- RSSIを計測する。
- RSSIの平均値や標準偏差を算出して、最終的に設定すべきTxPowerを決める
 
ただしここではここまでの実装や説明を行うかわりに、とりあえず Beacon の電波を受けて表示するだけのカンタンな処理を書いてみることにします。

# 使用するコンポーネント
やりたいことはDelphiでiBeaconの電波を受けた上で、以下のような情報をログに残すことです。

- Major
- Minor
- RSSI（受信強度）

このために必要なコンポーネントは以下の２つです。

- TBeacon
- TMemo

そして、TBeacon のイベントのうち、OnCalculateDistance に対してイベントハンドラを書きます。

この状態をフォームに配置した状態のスクリーンショットはこんなカンジですね。
![image](https://qiita-image-store.s3.amazonaws.com/0/149350/a564a533-7006-feb8-99bd-ba37f530e54e.png)

そして CalculateDistance にはこんな処理を書きます。１行しか書いていません。

```delphi:TForm1.Beacon1CalcutateDistance
procedure TForm1.Beacon1CalculateDistances(const Sender: TObject;
  const ABeacon: IBeacon; ATxPower, ARssi: Integer; var NewDistance: Double);
begin
  Memo1.Lines.Insert(0,Format('%d:%d %d', [ABeacon.Major, ABeacon.Minor, ARssi]) );
end;
```

TBeacon のプロパティですが、Enabled は True にしておいてください。これによりアプリの起動時から TBeacon が有効になります。

さて、これでビーコンを検出できるかというと、macOS や Android はこれだけでビーコンを検出できます。しかし iOS はこれではダメなのです。

iOS では UUID を明示的に指定された場合に限り、その UUID のビーコンが検出できるようになっています。だから iOS でビーコンの電波を受けるには、使用するビーコンの UUID を TBeacon に教えておく必要があるのです。

これは TBeacon のプロパティのうち、MonitorizedRegions で設定できます。また、構造ペインから MonitorizedRegions を選んで設定することもできます。スクリーンショットは MonitorizedRegions に対して監視対象のビーコンの情報を追加しようとしているところです。

![image](https://qiita-image-store.s3.amazonaws.com/0/149350/7b0270a2-8bed-ec58-2184-d58a8f5ad952.png)

MonitorizedRegions の追加を行うと、IDE の画面はこんなふうになっているはずです。
![image](https://qiita-image-store.s3.amazonaws.com/0/149350/adbc5a2d-b350-98ee-b983-8348100dd105.png)

ここで監視対象のビーコンの UUID をプロパティで設定します。すると設定した UUID を持つビーコンを検知できるようになります。

ここまで終わったら、ビーコンが動いていることを確認しつつ、作ったアプリを動かしてみます。するとこんなふうに TMemo に情報が出ているはずです。1:1 というのは Major:Minor のペアです。そして -xx という数値がビーコンの電波の受信強度 (RSSI） です。

![image](https://qiita-image-store.s3.amazonaws.com/0/149350/eafb7d4e-3db6-13ad-d1f1-3f45ea957e15.png)

ビーコンから1mの地点で電波強度 (RSSI) を測ることで、ビーコンに本来設定すべき TxPower の値が分かります。その値をビーコンに設定すれば距離測位の精度が改善されます。

# もうちょっとまともな測定を行うには

測定したRSSIをTMemoに書き出すだけではイマイチです。できれば、測定値の平均値をアプリで計算するとか、あるいは標準偏差（データの散らばり具合の指標）が見れるほうが良いし、さらにいうと測定値はグラフで見れるほうが便利です。

そして測定値に基づく TxPower の推奨値が分かればキャリブレーションが捗ります。

ここらへんのニーズを満たすための測定アプリのサンプルを GitHub の以下のリポジトリに配置しました。
https://github.com/kazinoue/BeaconCalibratorSimple

このアプリではキャリブレーションに基づく TxPower の補正後の値を自動的に算出します。

アプリ画面のスクリーンショットを貼っておきます。

![ビーコン探索画面](https://qiita-image-store.s3.amazonaws.com/0/149350/069aaa1c-9677-f6ac-9dcb-208e6fd87494.png)

![RSSI測定とTxPowerの補正値算出](https://qiita-image-store.s3.amazonaws.com/0/149350/1640aabb-9be6-3e00-a187-363be3f12fb4.png)


# 次回予告？
ビーコンのキャリブレーションが出来るようになりましたので、次はいよいよ、BeaconFenceを使って位置測位を行ってみましょう。

⇒ Delphi Advent Calendar 2016 17日目に「[固定Beaconを使って現在位置を測位するアプリをDelphi/C++BuilderとBeaconFenceで作る](http://qiita.com/kazinoue/items/99e46201bc9f3b81dfbe)」を書きました。