FL TIMESERVER の GNSS 機能を使って位置情報を取得してみる

はじめに

近年、工場やインフラ設備においては、
装置間で共通の「正確な時刻」を持つことがますます重要になっています。
ログ解析、トレーサビリティ、ネットワーク監視、セキュリティイベントの相関分析など、
あらゆる場面で「時刻のずれ」はトラブルの原因となり得ます。

Phoenix Contact の FL TIMESERVER NTP は、
こうした課題に対応するために開発された 産業用タイムサーバです。
GNSS(Global Navigation Satellite System)を利用して高精度な基準時刻を取得し、
NTP や NTS を用いてネットワーク内の機器へ安定した時刻同期を提供します。

本記事では、FL TIMESERVER NTP に搭載されている GNSS 機能を用いた位置情報の取得に焦点を当て、
GNSS(GPSを含む衛星測位システム)から取得できる 緯度・経度といった位置情報の内容や、
PC から実際にデータを取得し、画面上での確認および CSV ファイルへの出力を行う方法について解説します。

検証の構成イメージ

手順

サンプルpythonスクリプト

本手順及びサンプコードを実行したことによって発生したいかなる損害についても、当社は責任を
負いかねます。
運用システムへの適用や常時運用を行う場合は、十分な検証を行ったうえで、利用者の責任におい
て実施してください。

get_gps_position_with_log.py

import socket
import sys
import os
import csv
from datetime import datetime

HOST = '192.168.0.254'
PORT = 2947
VALID_MODES = ['decimal', 'dms']

def convert_to_decimal(coord, direction):
    if not coord or coord == '':
        return None
    split_at = coord.find('.') - 2
    degrees = int(coord[:split_at])
    minutes = float(coord[split_at:])
    decimal = degrees + minutes / 60
    if direction in ['S', 'W']:
        decimal *= -1
    return decimal

def decimal_to_dms(decimal, is_latitude=True):
    if is_latitude:
        direction = 'N' if decimal >= 0 else 'S'
    else:
        direction = 'E' if decimal >= 0 else 'W'

    decimal = abs(decimal)
    degrees = int(decimal)
    minutes_float = (decimal - degrees) * 60
    minutes = int(minutes_float)
    seconds = (minutes_float - minutes) * 60
    return f"{degrees}°{minutes}′{seconds:.1f}″ {direction}"

def check_args():
    if len(sys.argv) != 2 or sys.argv[1] not in VALID_MODES:
        print("表示形式を引数にて指定してください。")
        print("使用方法: python3 get_gps_position.py [decimal|dms]\n")
        print("【decimal】→ 10進数表示(例: 35.50627, 139.61501)")
        print("【dms】    → 度分秒表示(例: 35°30′22.9″ N, 139°36′54.0″ E)")
        sys.exit(1)
    return sys.argv[1]

def get_csv_path():
    yyyymmdd = datetime.now().strftime('%Y%m%d')
    filename = f"gps_{yyyymmdd}.csv"
    return os.path.join(os.getcwd(), filename)

def ensure_csv_header(csv_path):
    if not os.path.exists(csv_path):
        with open(csv_path, 'w', newline='', encoding='utf-8-sig') as f:
            writer = csv.writer(f)
            writer.writerow(["timestamp", "latitude_decimal", "longitude_decimal", "latitude_dms", "longitude_dms"])

def append_csv(csv_path, ts, lat_dec, lon_dec, lat_dms, lon_dms):
    with open(csv_path, 'a', newline='', encoding='utf-8-sig') as f:
        writer = csv.writer(f)
        writer.writerow([ts, f"{lat_dec:.6f}", f"{lon_dec:.6f}", lat_dms, lon_dms])

def receive_gps_data(mode):
    csv_path = get_csv_path()
    ensure_csv_header(csv_path)
    print(f"[CSV] 保存先: {csv_path}")

    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.connect((HOST, PORT))
        print(f"[接続成功] {HOST}:{PORT} に接続しました({mode}形式)\n")

        try:
            while True:
                data = s.recv(1024).decode(errors='ignore')
                for line in data.splitlines():
                    if line.startswith('$GNGGA'):
                        fields = line.split(',')
                        lat_raw = fields[2]
                        lat_dir = fields[3]
                        lon_raw = fields[4]
                        lon_dir = fields[5]

                        lat = convert_to_decimal(lat_raw, lat_dir)
                        lon = convert_to_decimal(lon_raw, lon_dir)

                        if lat is None or lon is None:
                            continue

                        ts = datetime.now().strftime('%Y-%m-%d %H:%M:%S')

                        lat_dms = decimal_to_dms(lat, is_latitude=True)
                        lon_dms = decimal_to_dms(lon, is_latitude=False)

                        if mode == 'decimal':
                            print(f"緯度: {lat:.6f}, 経度: {lon:.6f}")
                        else:
                            print(f"緯度: {lat_dms}, 経度: {lon_dms}")

                        append_csv(csv_path, ts, lat, lon, lat_dms, lon_dms)

        except KeyboardInterrupt:
            print("\n終了します。")

def main():
    mode = check_args()
    receive_gps_data(mode)

if __name__ == '__main__':
    main()
  • URLをコピーしました!