Edisonのセンサ
Edisonにはセンサがついてないので,様々なセンサを外付けしないと外界の情報を拾えません.
・・拾えません?
Wifiの情報取れるじゃーーーん!!
ジオロケーション
ジオロケーションというのは,地図上の位置推定のことです.GPSなんかが有名ですね.GPSは地球の周り遥か遠くを回っている静止衛星から送られて来る時刻情報を元に,一般相対性理論により位置推定を行う技術です.でも,今回は関係ない.
今回ターゲットにするのは,世の中にたくさん散らばっているWifiアクセスポイント(AP)の情報です.多くのAPは,あまり動かされることなく一地点でずっと電波を発し続けています.Wifi等で利用される電波は周波数が高いため,すぐに減衰してあまり遠くまで飛んでいきません.また,電波はその性質が距離に応じて強さが減衰することが知られています.
このあたりの性質を見て,Wifiの電波の強さによって,受信した機器の地図上の位置を特定できるんじゃないかと考えて人がいました.
そして,それを地球規模でやってしまったのがGoogleです.そしてなんと,私たちも,そのAPIを利用することができます.それが,Google MapのGeolocation API.APIのURLにJSONで固めた周辺のAP情報を送ると,すぐに推定位置の情報が返って来ます.
コード
Googleに開発者登録をして,Google MapのAPIキーを取得しておいてください.以下のコードのGAPI_KEYがそれにあたります.1000リクエスト/日,1リクエスト/秒以内なら,無償で使用可能だそうです.
以下のコードの.ELI_MACとなっているところには,位置推定から除外するAPのMACアドレスを入れます.私の場合は,Edisonの通信用に利用しているテザリング携帯のMACアドレスを入れています.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import commands, datetime, time
import re
import json, requests
GAPI_KEY = "fa1iuEe32hw4iusiWKgrwFEheERfieunGeWW"
ELI_MAC = ["12:34:56:78:9A:BC"]
re_mac = re.compile("\s+Cell \d{2} - Address: (\w{2}:\w{2}:\w{2}:\w{2}:\w{2}:\w{2})")
re_ch = re.compile("\s+Channel:(\d+)")
re_freq = re.compile("\s+Frequency:(\S+) GHz")
re_qual = re.compile("\s+Quality=(\S+) Signal level=(\S+) dBm")
re_essid = re.compile("\s+ESSID:\"([^\"]+)\"")
class AP:
def __init__(self, _mac, _channel, _frequency, _quality, _signal, _essid=""):
self.mac = _mac
self.channel = _channel
self.frequency = _frequency
self.quality = _quality
self.signal = _signal
self.essid = _essid
self.time = datetime.datetime.now()
def show(self):
print("%s, %s, %s, %s, %s, %s" % (self.essid, self.mac, self.channel, self.frequency, self.quality, self.signal))
def getVal(self):
t = datetime.datetime.now() - self.time
age = t.seconds * 1000 + t.microseconds / 1000
return {"macAddress":self.mac, "signalStrength":self.signal, "age":age, "channel":self.channel}
def getAPList():
aplist = []
res = commands.getoutput("/sbin/iwlist wlan0 scan")
essid = ""
mac = ""
channel = ""
frequency = ""
quality = ""
signal = ""
for t in res.splitlines():
m = re_mac.match(t)
if m:
mac = m.group(1)
continue
m = re_ch.match(t)
if m:
channel = m.group(1)
continue
m = re_freq.match(t)
if m:
frequency = m.group(1)
continue
m = re_qual.match(t)
if m:
quality = m.group(1)
signal = m.group(2)
if mac not in ELI_MAC:
aplist.append(AP(mac, channel, frequency, quality, signal, essid))
continue
m = re_essid.match(t)
if m:
essid = m.group(1)
continue
return aplist
def getLocationFromWifi(wifilist):
url = "https://www.googleapis.com/geolocation/v1/geolocate?key=%s" % GAPI_KEY
headers = {"Content-Type": "application/json"}
data = json.dumps({"wifiAccessPoints":wifilist})
r = requests.post(url, data=data, headers=headers)
geo = r.json()
return {"Latitude":geo['location']['lat'], "Longitude":geo['location']['lng'], "Accuracy":geo['accuracy']}
if __name__ == '__main__':
aps = {}
while True:
naps = getAPList()
lapsk = aps.keys()
napsk = []
# add new APs to AP list.
for ap in naps:
if ap.mac not in lapsk:
aps[ap.mac] = ap
# make new AP names list
napsk.append(ap.mac)
# search missed APs to delete from AP list
for ap in lapsk:
if ap not in napsk:
aps.pop(ap)
loc = getLocationFromWifi([d.getVal() for d in aps.values()])
print("Latitude: %f, Longitude: %f, Accuracy; %f" % (loc['Latitude'], loc['Longitude'], loc['Accuracy']))
print("https://www.google.co.jp/maps/@%s,%s,21z?hl=ja" % (loc["Latitude"], loc["Longitude"]))
time.sleep(30)
このコードを,管理者権限で実行してください.
テストのつもりで自宅で実行してみたら,部屋番号まで分かるんじゃないかってくらいなレベルで特定されて,ちょっと怖かったです.
実験
秋葉原-御茶の水間を徒歩で移動した際の位置推定の結果を以下に示します.赤点になっているのが,推定された位置です.なお,この実験は,APIの時間単位の制限を回避するため,APの情報だけ取得しておいて,位置推定のほうは帰宅してから行いました.
Edisonで周辺のWifi情報を収集しながら歩いていた際のGPSのデータは以下のようになっています.こちらも,そんなに精度が良いわけではないので,参考程度に.
考察
いきなり御茶の水駅前に赤点が現れたりしているところには笑ってしまいますが,びっくりするくらいドンピシャな位置を推定しているところもありますね.常識的な移動速度でフィルタをかければ,あるいは?
えぇ.実験場所に秋葉原を選んだのは,サンプルとなるWifi APの数が大量にあるだろうと踏んだからです.場所によっては60近くもAPが見えるところもあり,期待以上でした.
まとめ
- Edison単体でも地図上の位置推定はある程度可能.
- Big brother is watching YOU !!