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()