ブログ

Armadillo-IoT: GPSを使わずに位置情報を取得する

at_takenoshita
2015年11月24日 19時01分

持ち運び可能な、いわゆるモバイルデバイスで「位置情報」を取得する方法には、以下のようなものがあります。

  • GPSを用いる方法
  • Wi-Fi(無線LAN)のアクセスポイントの位置情報を用いる方法
  • 3G/LTEの基地局の位置情報を用いる方法

最も精度が高く、適用可能な範囲も広いのはGPSを用いる方法ですが、専用のモジュールやアンテナが必要となり、デバイスのコスト増の要因となります。Wi-Fiのアクセスポイントの位置情報を用いる方法は、GPSモジュールは不要ですが、適用可能範囲がアクセスポイントの電波が届く範囲に限られてしまいます。

Armadillo-IoTのように3G/LTEモジュールを積んだ製品では、モバイル通信が可能な範囲内では、接続している基地局の位置情報を用いて、ざっくりとした位置を知ることができます。GPSやWi-Fiを用いた方法と比べると精度は劣りますが、「だいたいどの辺りにあるか分かればいい」というユースケースもあるかと思いますので、本稿では、この方法について紹介します。

基地局の位置情報を調べる方法

携帯電話網の基地局には、基地局ID(Cell ID, CID)と呼ばれるIDが割り振られています。NTTドコモやKDDI、ソフトバンクなどの通信事業者は、当然ながらどのCIDがどこに位置しているかというデータベースを持っています。このデータベースへアクセスできれば、基地局が位置する場所が分かるのですが、そのためのAPIは、NTTドコモの場合SPモード契約端末にしか提供してないなど、制約があるようです。

通信事業者以外が提供するAPIとしては、Googleが提供する Google Map Geolocation API があります。Google Map Geolocation APIでは、Cell ID(CID)、Location Area Code (LAC)、Mobile Country Code (MCC)、Mobile Network Code(MNC)を渡せば、位置情報(緯度/経度)を返してくれるようです。

MCCとMNCは契約している回線事業者ごとに固定となっており、SIMが持つIMSI(International Mobile Subscriber Identity)に含まれます。そのため、IMSIとCID、LACが分かれば、位置情報を知ることができます。

各コードを調べる

Armadillo-IoT ゲートウェイ スタンダードモデル G2 (AG421-)の場合、3Gモジュール(Sierra Wireless製 HL8548)に対し、ATコマンドを発行することで、Google Map Geolocation APIが要求する各コードを調べることができます。条件として、SMS対応のSIMを使用する必要があります。また、基地局から情報を取得するため、APN/ユーザー名/パスワードなどを適切に設定し3Gのネットワークインターフェース(umts0)をupしておいてください。

シリアルコンソールから3Gモジュールに対してATコマンドを発行するには、tipコマンドを使用します。

[armadillo ~]$ tip -l /dev/ttyATCMD -s 115200
Connected.

IMSIは「AT+CIMI」で調べることができます。

AT+CIMI
44010xxxxxxxxxxx

OK

最初の3桁がMCC、次の2桁がMNCです。MCC=440、MNC=10となっていますので、この組み合わせはNTT DOCOMOを意味します。(参照: https://ja.wikipedia.org/wiki/Mobile_Network_Code )

CIDとLACは「AT+KCELL=0」で調べることができます。アットマークテクノの横浜営業所で実行した場合は、下記のような結果が得られました。

AT+KCELL=0
+KCELL: 0
+KCELL: 10,2,10736,44f001,81,4634020, ...(後略)

OK

LAC = 81(16進数)、CID = 4634020(16進数)となっています。

札幌本社だと、下記のようになります。

AT+KCELL=0
+KCELL: 0
+KCELL: 7,2,10736,44f001,40f,210802a, ...(後略)

OK

LAC = 40f(16進数)、CID = 210802a(16進数) となっています。

位置情報に変換する

取得したMCC、MNC、LAC、CIDをGoogle Map Geolocation APIに渡せば、位置情報を得ることができます。Google APIを使うには、Google APIのアカウントを開設し、APIキーを発行しておく必要があります。

Googe APIs Consoleにログインし、適当なプロジェクトを作成した後、「概要」から「Google Maps Geolocation API」を選択してください。

「APIを有効にする」ボタンをクリックして、Google Maps Geolocation APIを使えるようにします。

続いて、「認証情報」の「認証情報を追加」ボタンをクリックし、「APIキー」を選択します。「新しいキーの作成」というポップアップが表示されますので、「サーバーキー」を選択してください。すると、APIキーが発行されます。

以上の準備を行ったうえで、Armadillo-IoTで下記スクリプトを実行すると、位置情報を取得することができます。API_KEY変数には、発行したAPIキーを設定してください。

#!/bin/sh

API_KEY=""

URL="https://www.googleapis.com/geolocation/v1/geolocate?key=${API_KEY}"

MCC="440"
MNC="10"
LAC="129" #16進数で0x81
CID="73613344" #16進数で0x4634020

BODY=$(cat << EOF
{
  "cellTowers": [
    {
      "cellId": ${CID},
      "locationAreaCode": ${LAC},
      "mobileCountryCode": ${MCC},
      "mobileNetworkCode": ${MNC}
    }
  ]
}
EOF
)

curl -k -H "Content-type: application/json" -d "$BODY" "$URL"

正常に実行できたら、下記のような実行結果が得られます。

{
 "location": {
  "lat": 35.467575499999995,
  "lng": 139.6180681
 },
 "accuracy": 907.0
}

latとlngが緯度、経度になっていますので、Google Mapにクエリとして渡すと、どの位置に基地局があるのか確認することができます。

http://maps.google.com/maps?q=35.467575499999995,139.6180681

LACとCIDを取得するスクリプト

前掲のスクリプトを少し修正して、LACとCIDは決め打ちではなく都度取得するようにします。

#!/bin/sh

API_KEY=""

URL="https://www.googleapis.com/geolocation/v1/geolocate?key=${API_KEY}"

MCC="440"
MNC="10"

cell=$(/etc/config/3g-celllocation)
set -- $cell

LAC=$1
CID=$2

BODY=$(cat << EOF
{
  "cellTowers": [
    {
      "cellId": ${CID},
      "locationAreaCode": ${LAC},
      "mobileCountryCode": ${MCC},
      "mobileNetworkCode": ${MNC}
    }
  ]
}
EOF
)

curl -k -H "Content-type: application/json" -d "$BODY" "$URL"

/etc/config/3g-celllocation は下記のスクリプトにしてください。(chmod +x で実行権限を付けるのを忘れないように。)

#!/bin/sh

. /usr/share/3g/3g-common.sh

restore_stty_setting_handler
flock $SERIAL_3G_LOCK expect /etc/config/3g-celllocation.exp

さらに、そこから呼ばれる /etc/config/3g-celllocation.exp は下記のようにしてください。

#!/usr/bin/env expect

source /usr/share/3g/3g-function.exp
log_user 0

set lac -1
set cid -1

serial_connect 10
busy_wait 10

send_command "AT+KCELL=0"
set timeout 10

expect "+KCELL: 0"
expect -re "\\+KCELL: (\[^,\]+),(\[^,\]+),(\[^,\]+),(\[^,\]+),(\[^,\]+),(\[^,\]+),(.*)" {
        set lac [expr 0x$expect_out(5,string)]
        set cid [expr 0x$expect_out(6,string)]
}

puts "$lac $cid"

exit 0

以上、接続している基地局の情報(LAC及びCID)から、位置情報(緯度経度)を取得する方法についてご紹介しました。GPSのように精度よく測定できるものではありませんが、3G/LTE通信が出来さえすれば、おおよそどのあたりにデバイスが存在するかを知ることができるようになります。