8
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Mozilla Location Serive を使って周囲の WiFi AP の一覧から位置情報を取得する

Last updated at Posted at 2018-07-12

macOSのairportコマンドで周囲のWiFiをスキャンして、得られたBSSIDをMozilla Location Serviceに送って現在地を取得してみます。
MacBook Pro (13-inch, 2016, Two Thunderbolt 3 ports)で試しました。

最初に断っておきますがMacで位置情報を取得したいなら通常はCoreLocationを使うべきですし、ブラウザから位置情報を取得したいならGeolocation APIを使うべきです。
airportに限らずBSSIDの一覧を取得できるコマンド(Linuxならnmcliなど)とMozilla Location Serviceを組み合わせると、OSを問わずGPSなしで位置情報を取得できます。

airportで周囲のWiFiをスキャンする

airportコマンドは/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airportにあります。
なんでパスが通っていないのかは長年の疑問です。

コマンド
/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -s
実行結果
                            SSID BSSID             RSSI CHANNEL HT CC SECURITY (auth/unicast/group)
                 pr500m-9ec840-2 xx:xx:xx:xx:xx:xx -88  11      Y  JP WPA2(PSK/AES/AES) 
                 elecom5g-CBCA28 xx:xx:xx:xx:xx:xx -91  116     Y  US WPA2(PSK/AES/AES) 
                 A8A795523D8B-5G xx:xx:xx:xx:xx:xx -87  100     Y  JP WPA(PSK/AES/AES) WPA2(PSK/AES/AES) 
                  aterm-25d691-a xx:xx:xx:xx:xx:xx -37  36      Y  -- WPA(PSK/AES/AES) WPA2(PSK/AES/AES) 
                  aterm-c1ff43-g xx:xx:xx:xx:xx:xx -81  11      Y  JP WPA(PSK/AES/AES) WPA2(PSK/AES/AES) 
                penguin piyopiyo xx:xx:xx:xx:xx:xx -81  9       Y  -- WPA(PSK/AES/AES) WPA2(PSK/AES/AES) 
                W04_54B121E2BF7B xx:xx:xx:xx:xx:xx -46  9       Y  -- WPA(PSK/AES,TKIP/TKIP) WPA2(PSK/AES,TKIP/TKIP) 
                  リビング.o xx:xx:xx:xx:xx:xx -70  6       Y  -- NONE
                  aterm-58c56b-g xx:xx:xx:xx:xx:xx -78  4       Y  JP WPA(PSK/AES/AES) WPA2(PSK/AES/AES) 
                   ssw-pc-a1a9c9 xx:xx:xx:xx:xx:xx -81  1       Y  -- WPA(PSK/AES/AES) WPA2(PSK/AES/AES)

実際は上記コマンドに | sed 's/..:..:..:..:..:../xx:xx:xx:xx:xx:xx/g' と続けてBSSIDを伏せ字にしています。

1行目を削る

1行目はいらないので sed -n '2,$p' で削ります。

コマンド
/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -s |
sed -n '2,$p'
実行結果
                   ssw-pc-a1a9c9 xx:xx:xx:xx:xx:xx -80  1       Y  -- WPA(PSK/AES/AES) WPA2(PSK/AES/AES) 
                  aterm-58c56b-g xx:xx:xx:xx:xx:xx -83  4       Y  JP WPA(PSK/AES/AES) WPA2(PSK/AES/AES) 
                  4CE67673D86F-1 xx:xx:xx:xx:xx:xx -89  6       Y  -- WPA(PSK/AES/AES) 
                  aterm-c1ff43-g xx:xx:xx:xx:xx:xx -89  11      Y  JP WPA(PSK/AES/AES) WPA2(PSK/AES/AES) 
                 elecom5g-CBCA28 xx:xx:xx:xx:xx:xx -90  116     Y  US WPA2(PSK/AES/AES) 
                 A8A795523D8B-5G xx:xx:xx:xx:xx:xx -85  100     Y  JP WPA(PSK/AES/AES) WPA2(PSK/AES/AES) 
                  aterm-25d691-a xx:xx:xx:xx:xx:xx -41  36      Y  -- WPA(PSK/AES/AES) WPA2(PSK/AES/AES) 
                penguin piyopiyo xx:xx:xx:xx:xx:xx -83  9       Y  -- WPA(PSK/AES/AES) WPA2(PSK/AES/AES) 
                W04_54B121E2BF7B xx:xx:xx:xx:xx:xx -45  9       Y  -- WPA(PSK/AES,TKIP/TKIP) WPA2(PSK/AES,TKIP/TKIP) 
                  リビング.o xx:xx:xx:xx:xx:xx -68  6       Y  -- NONE
                 aterm-cba967-gw xx:xx:xx:xx:xx:xx -84  3       N  JP WEP
                  aterm-cba967-g xx:xx:xx:xx:xx:xx -85  3       Y  JP WPA(PSK/AES/AES) WPA2(PSK/AES/AES) 

BSSIDとRSSIだけ抜き出す

続いて、BSSIDとRSSIだけ抜き出します。
sed 's/^.*\(..:..:..:..:..:..\) *\(-[0-9]\{1,3\}\).*$/\1 \2/'とやったらできました。
正規表現がなかなかおぼえられないのでいつもここを見ています。

コマンド
/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -s |
sed -n '2,$p' |  # 1行目を削除する
sed 's/^.*\(..:..:..:..:..:..\)  *\(-[0-9]\{1,3\}\).*$/\1 \2/'  # BSSIDとRSSIだけ抜き出す
実行結果
xx:xx:xx:xx:xx:xx -87
xx:xx:xx:xx:xx:xx -85
xx:xx:xx:xx:xx:xx -88
xx:xx:xx:xx:xx:xx -77
xx:xx:xx:xx:xx:xx -93
xx:xx:xx:xx:xx:xx -83
xx:xx:xx:xx:xx:xx -86
xx:xx:xx:xx:xx:xx -41
xx:xx:xx:xx:xx:xx -48
xx:xx:xx:xx:xx:xx -80
xx:xx:xx:xx:xx:xx -79
xx:xx:xx:xx:xx:xx -80
xx:xx:xx:xx:xx:xx -40
xx:xx:xx:xx:xx:xx -22

JSONに変換する

Geolocate APIのリクエストボディは次のようなJSONです。

GeolocateAPIリクエストボディの例
{
    "wifiAccessPoints": [{
        "macAddress": "01:23:45:67:89:ab",
        "signalStrength": -51
    }, {
        "macAddress": "01:23:45:67:89:cd"
    }]
}

さきほどのSSIDの一覧をJSONに変換します。

コマンド
/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -s |
sed -n '2,$p' |
sed 's/^.*\(..:..:..:..:..:..\)  *\(-[0-9]\{1,3\}\).*$/\1 \2/' |
awk '{printf("{\"macAddress\": \"%s\", \"signalStrength\": %s}\n", $1, $2)}' |  # BSSIDとRSSIの組をJSONのオブジェクトに変換する
awk 'BEGIN{printf "["} {if(NR != 1){printf ","};printf $0} END{printf "]"}' | # オブジェクトを配列にまとめる
awk '{printf("{\"wifiAccessPoints\": %s}\n", $0)}'  # 外側のJSONを付け加える
実行結果
{"wifiAccessPoints": [{"macAddress": "xx:xx:xx:xx:xx:xx", "signalStrength": -81},{"macAddress": "xx:xx:xx:xx:xx:xx", "signalStrength": -88},{"macAddress": "xx:xx:xx:xx:xx:xx", "signalStrength": -43},{"macAddress": "xx:xx:xx:xx:xx:xx", "signalStrength": -82},{"macAddress": "xx:xx:xx:xx:xx:xx", "signalStrength": -83},{"macAddress": "xx:xx:xx:xx:xx:xx", "signalStrength": -80},{"macAddress": "xx:xx:xx:xx:xx:xx", "signalStrength": -52},{"macAddress": "xx:xx:xx:xx:xx:xx", "signalStrength": -72},{"macAddress": "xx:xx:xx:xx:xx:xx", "signalStrength": -76},{"macAddress": "xx:xx:xx:xx:xx:xx", "signalStrength": -82},{"macAddress": "xx:xx:xx:xx:xx:xx", "signalStrength": -39},{"macAddress": "xx:xx:xx:xx:xx:xx", "signalStrength": -22}]}

curlでHTTPリクエストを行う

curlはリクエストボディの内容を標準入力から受け取れるので、さきほどの結果をcurlに渡して、実際にリクエストを送ってみます。

APIのURLは、https://location.services.mozilla.com/v1/geolocate?key=<API_KEY>で、開発中などに試しに使ってみるときはAPI_KEYにはtestといれておけばよいようです。

コマンド
/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -s |
sed -n '2,$p' |
sed 's/^.*\(..:..:..:..:..:..\)  *\(-[0-9]\{1,3\}\).*$/\1 \2/' |
awk '{printf("{\"macAddress\": \"%s\", \"signalStrength\": %s}\n", $1, $2)}' |
awk 'BEGIN{printf "["} {if(NR != 1){printf ","};printf $0} END{printf "]"}' |
awk '{printf("{\"wifiAccessPoints\": %s}\n", $0)}' |
curl -H "Content-type: application/json" -d @- https://location.services.mozilla.com/v1/geolocate?key=test
実行結果
{"location": {"lat": 35.xxxxxxx, "lng": 139.xxxxxxx}, "accuracy": 25.7291953}

緯度経度が取得できました。
OpenStreetMapに緯度経度を入力してみたら、accuracyで示されているくらいの精度で現在地が表示されました。
これは良い・・・

ついでなのでOSMのページをブラウザで開くところまで自動化してみます。

周囲のWiFiをスキャンしてGeolocationAPIで緯度経度を取得して現在地をブラウザで表示する
/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -s |
sed -n '2,$p' |
sed 's/^.*\(..:..:..:..:..:..\)  *\(-[0-9]\{1,3\}\).*$/\1 \2/' |
awk '{printf("{\"macAddress\": \"%s\", \"signalStrength\": %s}\n", $1, $2)}' |
awk 'BEGIN{printf "["} {if(NR != 1){printf ","};printf $0} END{printf "]"}' |
awk '{printf("{\"wifiAccessPoints\": %s}\n", $0)}' |
curl -H "Content-type: application/json" -d @- https://location.services.mozilla.com/v1/geolocate?key=test |
sed 's/^.*"lat": \([0-9][0-9]*\.[0-9][0-9]*\).*"lng": \([0-9][0-9]*\.[0-9][0-9]*\).*$/\1 \2/' |
awk '{printf("https://www.openstreetmap.org/#map=19/%s/%s\n", $1, $2)}' |
xargs open
8
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?