NanoPiNEOはRTCを搭載しておらず、LAN未接続状態ではNTPによる時刻合わせもできない。
このGNSS地形スキャナーはGNSS座標データをタイムスタンプで管理するため、ある程度正確な時刻が必要。
LAN未接続のスタンドアロンで使用する場合、別の時刻合わせの手段を用意したい。
GNSSデータの活用
GNSS受信機は絶えずNMEAフォーマットの情報をGNSSから受信している。
NMEA情報の中には時刻情報だけでなく年月日情報も埋め込まれているものがある。
$GPRMCで始まるNMEA情報がそれ。
ネット接続していないスタンドアロン状態でも、GNSS受信機によりNMEA情報を受信している場合、そこから時刻情報を取得できる。
Pythonでプログラム
実際は下記のようなコードで実現
$ cat work_gnssdate_006.py
#!/usr/bin/python3
import sys
import subprocess
import re
from datetime import datetime, timedelta, timezone
import serial
timezone = 9
dtnow = datetime.now()
print(dtnow)
ser = serial.Serial(“/dev/ttyACM0”, 115200, timeout=2)
while True:
rsvline = ser.readline()
if rsvline[0] != ord(“$”):
ser.reset_input_buffer()
continue
try:
line = rsvline.decode(‘ascii’)
except Exceptin as e:
print(e)
ser.reset_input_buffer()
continue
line = line.strip()
items = line.split(“,”)
if re.search(“\$..RMC”,items[0]):
print(line)
(dummy1,day,month,year,dummy2)= re.split(“(..)(..)(..)”,items[9])
((dummy1,hour,minute,sec,msec10,dummy2) = re.split(“(..)(..)(..)\.(.*)”,items[1])
print(“20%s/%s/%s %s:%s:%s.%s”%(year,month,day,hour,minute,sec,msec10))
dt = datetime(2000 + int(year),int(month),int(day),
int(hour),int(minute),int(sec),int(msec10)*10000) + timedelta(hours=timezone)
if dt > dtnow:
ser.close()
gnsstime = dt.strftime(“%Y-%m-%d %H:%M:%S”)
subprocess.call([“/usr/bin/timedatectl”, “set-time”, gnsstime])
dtnow = datetime.now()
print(dtnow)
sys.exit(0)
ser.reset_input_buffer()
コードメモ(pythonはまだよく慣れていないので):
・シリアル通信の受信方法
import serial
ser = serial.Serial(“/dev/ttyACM0”, 115200, timeout=2)
rsvline = ser.readline()
ser.reset_input_buffer()
ser.close()
readline()メソッドの戻り値はbytes型
bytes型はバイナリデータ列で変数名に[]を付けることで1byte単位で取り出せる
・文字列の位置指定
rsvline[0]
変数rsvlineに格納されている文字列の1文字目
・文字列をUnicode値に変換
ord(“$”)
・bytes型をstr型に変換
line = rsvline.decode(‘ascii’)
decode()メソッドの引数のデフォルトはutf-8
utf-8の場合、下記エラーが発生するためasciiにしている
‘utf-8’ codec can’t decode byte 0xf4 in position 3: invalid continuation byte
・NMEAフォーマットの正規表現による判定
import re
re.search(“\$..RMC”,items[0]):
reは正規表現モジュール
re.searchメソッドは指定した正規表現パターンにマッチする部分が文字列全体にあるかを調べて、マッチする場合はre.Matchオブジェクトを返す
「\$」$はメタ文字の$ではなく、一般文字の$
「.」任意の1文字
つまり「$〇〇RMC」という文字列が存在する場合はMatchオブジェクトを返すという意味
・NMEAから時刻情報を抽出
items = line.split(“,”)
lineにはRMCヘッダのNMEAデータが格納
データ例:「$GNRMC,084021.80,A,3551.4838915,N,13957.8312430,E,0.071,,091023,,,A,V*12」
これをカンマ区切りで配列itemsへ格納
(dummy1,day,month,year,dummy2)= re.split(“(..)(..)(..)”,items[9])
items[9]は上の例では「091023」
re.splitメソッドは正規表現で文字列を分割し戻り値はアンパックできる
※アンパック:コレクションの要素をまとめて変数に代入する手法
正規表現にキャプチャグループを使用する場合はグループ毎に分割してそれぞれタプルへ格納される
※タプル:(dummy1,day,month,year,dummy2)という表現のこと
正規表現にキャプチャグループを使うと、前後2か所に空文字も分割されてしまうため、dummy1とdummy2で受けている
・時間、日付情報をdatetime型変数に格納
dt = datetime(2000 + int(year),int(month),int(day),int(hour),int(minute),int(sec),int(msec10)*10000) + timedelta(hours=timezone)
・datetime変数を時刻変更のlinuxコマンドのフォーマット文字列へ変換してシステム時間を変更
gnsstime = dt.strftime(“%Y-%m-%d %H:%M:%S”)
subprocess.call([“/usr/bin/timedatectl”, “set-time”, gnsstime])
実行結果
$ sudo python3 work_gnssdate_006.py
‘2023-10-29 16:38:45.711376
$GNRMC,074053.40,A,3551.4842962,N,13957.8336696,E,0.031,,291023,,,A,V*17
‘2023/10/29 07:40:53.40
‘2023-10-29 16:40:53.003235