今ココなう!のPythonクライアント作った

woman programming on a notebook 作ってみた
スポンサーリンク

周りにはiPhoneのネイティブアプリ版を早く作れ!と言われてますが、時間がかかりそう(位置情報をポストするまではできたけど、ソフトとして完成させるとなるとと物足りないよねー)なので、簡単そうなPythonクライアント作ってみた。

簡単というのは、以前から自作のPythonクライアントを自分のライブトラッキングサーバ用に作っていたので、それを今ココなう!のAPI仕様にあわせるだけだったり。

確認してないですが、Python実行環境を導入して、スクリプトを少し改造さえすればMacやLinuxなどでも使えると思うので、もしよかったら自由にお使いください。
あと、自分のGPSロガーでしか確認していません、ごめんなさい。

ソース

#!/usr/bin/python
# -*- coding: utf-8 -*-

# pySerial + Win32 Extensions(optional)
import sys, os, os.path, serial, urllib, urllib2, socket, thread, time, winsound

# Httpのタイムアウト値
socket.setdefaulttimeout(10)

# シリアルポートの設定
serialPort = 'COM6'


# 今ココなう!のユーザーIDとパスワード
userId = 'USERID'
userPassword = 'USERPASSWORD'

saveFlag = 1
markerType = 0

tryCount = 0
baseTime = 0

# NMEAセンテンスの読み取り
def getNmeaSentence():
    global baseTime

    while True:
        try :
            buffer = ser.read(1000)
        except :
            print "error"
            return "error"
        
        if int(time.time()) + baseTime - time.time() > 0:
            time.sleep(int(time.time()) + baseTime - time.time() )

        # タイミング調整
        if buffer[0:6] != '$GPGGA':
            time.sleep(0.1)
            if baseTime > 0.9:
                baseTime -= 0.9
            else:
                baseTime += 0.1
            print "clock-timing adjustment...\n"
            return

        # GGAセンテンスの測位時刻の秒一桁が0
        if buffer[12:13] == '0':
            lines = buffer.split("\r\n")

            data = [''] * 12
            
            for line in lines:
                nmea = line.split(',')
                
                if nmea[0] == '$GPGGA':
                    data[0] = nmea[1] # 測位時刻
                    data[1] = nmea[2] # 緯度
                    data[2] = nmea[3] # 北緯・南緯
                    data[3] = nmea[4] # 経度
                    data[4] = nmea[5] # 東経・西経
                    data[5] = nmea[6] # GPS測位状態
                    data[6] = nmea[7] # 受信衛星数
                    data[7] = nmea[8] # HDOP
                    data[8] = nmea[9] # 高度
                elif nmea[0] == '$GPRMC':
                    data[9] = nmea[7] # 対地速度 
                    data[10] = nmea[8] # 進行方向
                    data[11] = nmea[9] # 測位日付
            
            # GPSが受信不能
            if data[5] == '0':
                print "not receivable GPS data..."
                winsound.PlaySound("SystemQuestion", winsound.SND_ALIAS)
                return
            
            args = (data,)
            thread.start_new_thread(putGpsData, args)


# Post
def putGpsData(args) :

    global tryCount

    print "post..."

    gpsTime = time.strftime("%Y-%m-%dT%H:%M:%S", time.localtime())
    gpsLatitudeD = int(float(args[1]) / 100)
    gpsLatitudeM = (float(args[1]) - gpsLatitudeD * 100)
    gpsLatitude = gpsLatitudeD + gpsLatitudeM / 60
    if args[2] == 'S':
        gpsLatitude *= -1
    gpsLongitudeD = int(float(args[3]) / 100)
    gpsLongitudeM = (float(args[3]) - gpsLongitudeD * 100)
    gpsLongitude = gpsLongitudeD + gpsLongitudeM / 60
    if args[4] == 'W':
        gpsLongitude *= -1
    gpsSpeed = float(args[9]) * 1.852 # knotをkm/hに変換
    
    params = {
        "time" : gpsTime,
        "lat" : gpsLatitude,
        "lon" : gpsLongitude,
        "gpsq" : args[5],
        "gpsn" : args[6],
        "gpsh" : args[8],
        "gpsd" : args[10],
        "gpsv" : gpsSpeed,
        "save" : saveFlag,
        "t" : markerType,
    }
    
    topLevelUrl = 'imakoko-gps.appspot.com'
    url = 'http://imakoko-gps.appspot.com/api/post'
    passMan = urllib2.HTTPPasswordMgrWithDefaultRealm()
    passMan.add_password(None, topLevelUrl, userId, userPassword)
    authHandler = urllib2.HTTPBasicAuthHandler(passMan)
    opener = urllib2.build_opener(authHandler)
    urllib2.install_opener(opener)
    
    try:
        urllib2.urlopen(url, urllib.urlencode(params))
    except urllib2.HTTPError, e:
        winsound.PlaySound("Notify", winsound.SND_ALIAS)
        print e
    except urllib2.URLError, e:
        winsound.PlaySound("Notify", winsound.SND_ALIAS)
        print e
        
    tryCount = tryCount + 1
    
    print tryCount
    print params
    print "\n"

# シリアル接続
def connect() :
    global ser, baseTime

    try:
        ser = serial.Serial(serialPort, 38400, bytesize=8, parity='N', stopbits=1, timeout=0.9, xonxoff=0, rtscts=0)
    except serial.SerialException, e:
        print e

    baseTime = time.time() - int(time.time());
    
    while True:
        if getNmeaSentence() == "error":
            ser.close()
            return

    ser.close()

# メインループ
while True:
    connect()
    time.sleep(5)
    print "reconnect..."

解説

導入するもの

  • Python実行環境
  • pySerial
  • Win32 Extension(場合によっては必要ないかも)

やっていること

  1. メインでGPS接続試行でぐるぐる
  2. GPSにシリアル接続(ボーレートとかは調節してね。なんでタイムアウトが0.9なのかは謎)
  3. 1秒ごとにNMEAセンテンスを読み取る。読み取るタイミングを調節
  4. 調節がうまくいったら、10秒ごとにGGAとRMCのセンテンスを読み取る
  5. BASIC認証で今ココなう!APIにPOSTする

ぼくの場合、自転車での使用を想定していて、メッセージを目視することはできないので、なんらかのエラーが起きた場合に視覚以外で知る必要があります。なので、GPS情報が取得できない場合と、POSTがうまくいかなかった場合はそれぞれサウンドがなるように設定しています。

以上です。

Pythonがある程度わかる人前提なので、そんなに説明することないですね。逆に、なんでこんなシリアル接続パラメーターなのかとか、タイミングの取り方でもっとスマートなやり方とか、スレッド使うときにタプルで引数を渡すのはなんで?とか教えてもらいたかったり。

今ココなう!実装済み環境

  • Android Chrome Lite + Google Gears、各種ブラウザ + Google Gearsアドオン
  • iPhone Mobile Safari (OS 3.0以上)、Firefox 3.5以上、Safari 3.0以上
  • Python New!
タイトルとURLをコピーしました