anyPiの構成品である3Gpiを使ってGPSデータを入力し、この位置情報をネット上に設置したサーバにjson形式でアップロードします。「anyPiでGPSデータの取得」では、GPS service daemon「gpsd」を使用し、C言語からライブラリ「libgps 」を呼び出すプログラムを作成しましたが、今回はPython3を使用し、gpsdのクライアント「gps3」パッケージを用いてGPSデータを取得します。3Gpiを使ってGPSデータの入力を行ったハードウェアをつぎに示します。

3Gpiを使ったGPSデータの入力

Python3でGPSデバイスを使用するための環境構築

「/etc/default/gpsd」により、gpsdのデバイス名やgpsd実行時のオプションを確認します。

$ vi /etc/default/gpsd
# Default settings for the gpsd init script and the hotplug wrapper.

# Start the gpsd daemon automatically at boot time
START_DAEMON="true"

# Use USB hotplugging to add new USB devices automatically to the daemon
USBAUTO="true"

# Devices gpsd should collect to at boot time.
# They need to be read/writeable, either by user gpsd or the group dialout.
DEVICES="/dev/gps"

# Other options you want to pass to gpsd
GPSD_OPTIONS="-b"

次のコマンドで、gpsdサービスの状態を確認します。

$ sudo systemctl status gpsd.socket
 砒sd.socket - GPS (Global Positioning System) Daemon Sockets
   Loaded: loaded (/lib/systemd/system/gpsd.socket; enabled; vendor preset: enabled)
   Active: active (running) since Tue 2018-08-07 18:16:41 JST; 27min ago
   Listen: /var/run/gpsd.sock (Stream)
           [::1]:2947 (Stream)
           127.0.0.1:2947 (Stream)

Aug 07 18:16:41 raspberrypi systemd[1]: Listening on GPS (Global Positioning System) Daemon Sockets.

次のコマンドを実行し、GPSアンテナを有効にします。

$ sudo 3gpictl --gpson

Python3でGPSを使用するために、「Python 2.7-3.5 clients for gpsd」を次のコマンドでインストールします。

$ pip3 install gps3

<参考>
次のコマンドを使用して、GPSアンテナから入力されたGPSデータをモニタリングできます。

$ cgps -s

GPSデータをネット上にアップロードするPythonスクリプトの作成

次に示すPythonスクリプトにより、GPSのデータを取得し、そのデータを解析してJson形式にフォーマット化してネット上にアップロードします。

  • GPSDSocket はGPSDソケット接続を生成して、GPS出力の要求を行います。
  • DataStreamは、gpsdデータストリームをオブジェクト属性値にフォーマット化します。
  • HTTPSのURLをアクセスすると、SSL認証エラー「urllib.error.URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:646)>」が発生します。開発環境にアクセスするだけのコードだったので、14行目に「ssl._create_default_https_context = ssl._create_unverified_context
    」を記述します。

gpssample.py

from gps3 import gps3
import urllib.request, json
import ssl

if __name__ == '__main__':
  gps_socket = gps3.GPSDSocket()
  data_stream = gps3.DataStream() 
  gps_socket.connect()
  gps_socket.watch()

  url = "https://xxx.jp/gps/index.php" 
  method = "POST"
  headers = {"Content-Type" : "application/json"}
  ssl._create_default_https_context = ssl._create_unverified_context


  for new_data in gps_socket:
    if new_data:
      data_stream.unpack(new_data)
      print('time : ', data_stream.TPV['time'])
      print('lat : ', data_stream.TPV['lat'])
      print('lon : ', data_stream.TPV['lon'])
      print('alt : ', data_stream.TPV['alt'])
      print('speed : ', data_stream.TPV['speed'])
      # PythonオブジェクトをJSONに変換する
      obj = {"time" : data_stream.TPV['time'],
             "lat" : data_stream.TPV['lat'],
             "lon" : data_stream.TPV['lon'],
             "alt" : data_stream.TPV['alt'],
             "speed" : data_stream.TPV['speed']} 
             
      json_data = json.dumps(obj).encode("utf-8")

      # httpリクエストを準備してPOST
      request = urllib.request.Request(url, data=json_data, method=method, headers=headers)
      with urllib.request.urlopen(request) as response:
        response_body = response.read().decode("utf-8")
        print(response_body)

GPSデータをネット上にアップロードするPythonスクリプトの実行

次に示す「index.php」は、URL「https://xxx.jp/gps/index.php」に配置され、Json形式で受け取ったデータを「var_dump」コマンドによりアップロードのレスポンスとして戻し、プログラムの確認のために使用します。

index.php

<?php
try {
	$headers = getallheaders();
	$post_body = file_get_contents('php://input');
	
	$fp = fopen("samplepost.txt", "w");
	foreach ($headers as $name => $value) {
		fwrite($fp,"$name: $value\n");
	}
	fwrite($fp,"$post_body\n");
	fclose($fp);
	

	$obj = json_decode($post_body);  // オブジェクト形式
	if ($obj === NULL) return;
	var_dump($obj);
} catch (PDOException $e){
    var_dump($e->getMessage());
}
?>

作成したPythonスクリプト「gpssample.py」を次のコマンドで実行します。GPSからの位置情報が表示され、ネット上にJson形式でアップロードされて、そのデータが応答として表示されます。

$ python3 gpssample.py
time :  n/a
lat :  n/a
lon :  n/a
alt :  n/a
speed :  n/a
object(stdClass)#1 (5) {
  ["alt"]=>
  string(3) "n/a"
  ["time"]=>
  string(3) "n/a"
  ["speed"]=>
  string(3) "n/a"
  ["lon"]=>
  string(3) "n/a"
  ["lat"]=>
  string(3) "n/a"
}

time :  2018-08-16T06:20:35.700Z
lat :  35.43245065
lon :  139.64630665
alt :  48.1
speed :  0.0
object(stdClass)#1 (5) {
  ["alt"]=>
  float(48.1)
  ["time"]=>
  string(24) "2018-08-16T06:20:35.700Z"
  ["speed"]=>
  float(0)
  ["lon"]=>
  float(139.xx)
  ["lat"]=>
  float(35.xx)
}


time :  2018-08-16T06:20:35.700Z
lat :  35.xx
lon :  139.xx
alt :  48.1
speed :  0.0
object(stdClass)#1 (5) {
  ["alt"]=>
  float(48.1)
  ["time"]=>
  string(24) "2018-08-16T06:20:35.700Z"
  ["speed"]=>
  float(0)
  ["lon"]=>
  float(139.xx)
  ["lat"]=>
  float(35.xx)
}