【Mosaic-HAT】Raspberry Pi+RTKLIBでRTK移動局を作ってみよう

ラズパイ基準局(Base)が動いたので、次は移動局(Rover)を設定します。
※基準局の作成はこちら。<https://qiita.com/KIT-tokunaga/items/5df5854f1471528f31ee>
※ラズパイの拡張コネクタのシリアルポートを有効にすることを忘れずに。これも基準局のほうで説明しています。

RTK基準局の役割は、Mosaic-HATが自己位置とGNSS受信位置の差分情報や衛星情報などを標準化されているメッセージ(RTCM, NMEAなど)でシリアルに垂れ流すので、これをRTKLIB/str2strなどでRTK2goなどのNtripサーバに送って参照できるようにすることでした。対してRTK移動局は、Ntripサーバからこれら情報を収集し、Mosaic-HATのGNSS受信位置やそれに使った衛星情報と突き合わせて位置補正します。この突き合わせて補正する仕事を、RTKLIB/rtkrsvがやってくれます。つまり、Mosaic-HATに対しては、突き合わせに必要なメッセージをシリアルに流してもらう設定をすればよいということです。

#１．RTK移動局の設定

Mosaic-HATへのコマンド送信を行います。
基準局の設定で行ったリセットとCOMの設定は、同じものを使います。
その後に、S連打でコマンドモードに移し、そこで送信させるメッセージとその周期を設定します。`setRTCMv3Interval`が周期設定、`setRTCMv3Output`が送信させるメッセージの選択です。
で、メッセージを選びます。今回使っているRTCMv3フォーマットは、国土地理院が公開している「平成23年度マルチGNSS解析技術等の開発にむけた衛星系、受信機及びGNSS解析ソフトウェアの技術仕様調査」<https://www.gsi.go.jp/common/000068240.pdf>に詳しく載っています。しかしよくわからない。。。
そんなときは、RTK2goの参照するマウントポイントが、どのメッセージを配信しているのかを調べます。<http://www.rtk2go.com:2101/>には、マウントポイントと配信しているメッセージ番号および周期(カッコの数字、単位は秒)が記してあります。これと同じものをMosaic-HATに出力させれば、突き合わせできます！

先に設定したMosaic-HATの基準局とセットで運用したいところですが、まだ信用に欠けるので、とりあえず運用されている近場のマウントポイントを探します。私は石川県におり、このあたりだとGODAI_RTCM3さんが確実です（利用させてもらうときには一言連絡することをお勧めします。<https://rtk.silentsystem.jp/>に連絡情報あります）。RTK2goのGODAI_RTCM3の欄には、1004(1),1019(4)とあります。1004はGPS L1/L2の観測データ、1019は衛星エフェメリス(軌道)情報だそうです。周期はそれぞれ1秒と4秒。とりあえず両方1秒周期でMosaic-HATに出させます。
解説が長くなりましたが、リセットからの移動局設定スクリプトです。

```#python3:mosaic_Rover.py
import serial
import RPi.GPIO as GPIO
from time import sleep

#Module Reset
print("Module Reset")
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)

GPIO.setup( 5, GPIO.OUT, initial=GPIO.LOW)
sleep(1)
GPIO.output( 5, GPIO.HIGH)
GPIO.setup( 5, GPIO.IN)
sleep(10)

#COM Initialize
print("COM Initialize")
ser = serial.Serial('/dev/ttyS0', 115200)
sleep(1)        # wait for connection

#Module Initialize
def PutCmd(command) :
        ser.write(command)
        while 1 :
                msg = ser.readline().decode()
                if '$R' in msg :
                        print(msg)
                        break

print("Module Initialize")
ser.write(b'SSSSSSSSSSSSS\n')   # push MOSAIC to run in command mode
sleep(1)

PutCmd(b'setRTCMv3Interval, RTCM1004, 1\n')
PutCmd(b'setRTCMv3Interval, RTCM1019, 1\n')
PutCmd(b'setRTCMv3Output, COM1, RTCM1004+RTCM1019\n')
PutCmd(b'setDataInOut, COM1, , RTCMv3\n')

ser.write(b'setCOMSettings, COM1, baud19200\n')
sleep(1)

print("Finish")
ser.close()
```

#２．RTKLIBを使った高精度測位設定

いよいよ高精度測位です。
前節に書いたように、RTKLIBのrtkrsvがNtrip経由の基準局情報と移動局のGNSS情報を突き合わせて、位置を補正してくれます。
rtkrsvは`RTKLIB/app/rtkrsv/gcc `にあります。その１つ上のディレクトリに、重要な設定ファイルmyrtkbl.conがあります。これが要です。このファイルに一通りの設定をします。ポイントは、inpstr1をMosaic-HATのシリアルに合わせ、inpstr2をRTK2goに合わせます。下の方に、Baseのアンテナ位置を設定するところもあるので、マウントポイントの位置情報を転記します。その他の細かいところは、気にしないでください。

```#:myrtkbl.conf
# rtkrcv options for rtk (2010/08/07, v.2.4.0)

console-passwd     =admin
console-timetype   =utc        # (0:gpst,1:utc,2:jst,3:tow)
console-soltype    =deg        # (0:dms,1:deg,2:xyz,3:enu,4:pyl)
console-solflag    =1          # (0:off,1:std+2:age/ratio/ns)
inpstr1-type       =serial     # (0:off,1:serial,2:file,3:tcpsvr,4:tcpcli,7:ntripcli,8:ftp,9:http)
inpstr2-type       =ntripcli   # (0:off,1:serial,2:file,3:tcpsvr,4:tcpcli,7:ntripcli,8:ftp,9:http)
inpstr3-type       =off        # (0:off,1:serial,2:file,3:tcpsvr,4:tcpcli,7:ntripcli,8:ftp,9:http)
inpstr1-path       =ttyS0:19200:8:n:1:off       # ttyACM0:115200:8:n:1:off
inpstr2-path       =rtk2go.com:2101/GODAI_RTCM3
inpstr3-path       =
inpstr1-format     =rtcm3      # (0:rtcm2,1:rtcm3,2:oem4,3:oem3,4:ubx,5:ss2,6:hemis,7:skytraq,8:sp3)
inpstr2-format     =rtcm3      # (0:rtcm2,1:rtcm3,2:oem4,3:oem3,4:ubx,5:ss2,6:hemis,7:skytraq,8:sp3)
inpstr3-format     =           # (0:rtcm2,1:rtcm3,2:oem4,3:oem3,4:ubx,5:ss2,6:hemis,7:skytraq,8:sp3)
inpstr2-nmeareq    =off        # (0:off,1:latlon,2:single)
inpstr2-nmealat    =0          # (deg)
inpstr2-nmealon    =0          # (deg)
outstr1-type       =file       # (0:off,1:serial,2:file,3:tcpsvr,4:tcpcli,6:ntripsvr)
outstr2-type       =file       # (0:off,1:serial,2:file,3:tcpsvr,4:tcpcli,6:ntripsvr)
outstr1-path       =/home/pi/sol.pos
outstr2-path       =/home/pi/%Y%m%d_%h%M_sol.pos
outstr1-format     =llh        # (0:llh,1:xyz,2:enu,3:nmea)
outstr2-format     =llh        # (0:llh,1:xyz,2:enu,3:nmea)
logstr1-type       =file       # (0:off,1:serial,2:file,3:tcpsvr,4:tcpcli,6:ntripsvr)
logstr2-type       =off        # (0:off,1:serial,2:file,3:tcpsvr,4:tcpcli,6:ntripsvr)
logstr3-type       =off        # (0:off,1:serial,2:file,3:tcpsvr,4:tcpcli,6:ntripsvr)
logstr1-path       =/home/pi/%Y%m%d_%h%M_rov.log
logstr2-path       =
logstr3-path       =
misc-svrcycle      =10         # (ms)
misc-timeout       =30000      # (ms)
misc-reconnect     =30000      # (ms)
misc-nmeacycle     =5000       # (ms)
misc-buffsize      =32768      # (bytes)
misc-navmsgsel     =rover      # (0:all,1:rover,1:base,2:corr)
misc-startcmd      =./rtkstart.sh
misc-stopcmd       =./rtkshut.sh
file-cmdfile1      =../../../data/ubx_m8t_bds_raw_5hz.cmd
file-cmdfile2      =../../../data/oem4_raw_1hz.cmd
file-cmdfile3      =
pos1-posmode       =kinematic  # (0:single,1:dgps,2:kinematic,3:static,4:movingbase,5:fixed,6:ppp-kine,7:ppp-static)
pos1-frequency     =l1         # (1:l1,2:l1+l2,3:l1+l2+l5)
pos1-soltype       =forward    # (0:forward,1:backward,2:combined)
pos1-elmask        =20         # (deg)
#pos1-snrmask      =0          # (dBHz)
pos1-dynamics      =on         # (0:off,1:on)
pos1-tidecorr      =off        # (0:off,1:on)
pos1-ionoopt       =brdc       # (0:off,1:brdc,2:sbas,3:dual-freq,4:est-stec)
pos1-tropopt       =saas       # (0:off,1:saas,2:sbas,3:est-ztd,4:est-ztdgrad)
pos1-sateph        =brdc       # (0:brdc,1:precise,2:brdc+sbas,3:brdc+ssrapc,4:brdc+ssrcom)
pos1-exclsats      =           # (prn ...)
pos1-navsys        =31          # (1:gps+2:sbas+4:glo+8:gal+16:qzs+32:comp)
pos2-armode        =fix-and-hold # (0:off,1:continuous,2:instantaneous,3:fix-and-hold)
pos2-gloarmode     =off        # (0:off,1:on,2:autocal)
pos2-arthres       =3
pos2-arlockcnt     =0
pos2-arelmask      =0          # (deg)
pos2-aroutcnt      =5
pos2-arminfix      =10
pos2-slipthres     =0.05       # (m)
pos2-maxage        =30         # (s)
pos2-rejionno      =30         # (m)
pos2-niter         =1
pos2-baselen       =0          # (m)
pos2-basesig       =0          # (m)
out-solformat      =llh        # (0:llh,1:xyz,2:enu,3:nmea)
out-outhead        =on         # (0:off,1:on)
out-outopt         =off        # (0:off,1:on)
out-timesys        =jst        # (0:gpst,1:utc,2:jst)
out-timeform       =hms        # (0:tow,1:hms)
out-timendec       =3
out-degform        =deg        # (0:deg,1:dms)
out-fieldsep       =
out-height         =ellipsoidal # (0:ellipsoidal,1:geodetic)
out-geoid          =internal   # (0:internal,1:egm96,2:egm08_2.5,3:egm08_1,4:gsi2000)
out-solstatic      =all        # (0:all,1:single)
out-nmeaintv1      =0          # (s)
out-nmeaintv2      =0          # (s)
out-outstat        =off        # (0:off,1:state,2:residual)
stats-errratio     =100
stats-errphase     =0.003      # (m)
stats-errphaseel   =0.003      # (m)
stats-errphasebl   =0          # (m/10km)
stats-errdoppler   =1          # (Hz)
stats-stdbias      =30         # (m)
stats-stdiono      =0.03       # (m)
stats-stdtrop      =0.3        # (m)
stats-prnaccelh    =1          # (m/s^2)
stats-prnaccelv    =0.1        # (m/s^2)
stats-prnbias      =0.0001     # (m)
stats-prniono      =0.001      # (m)
stats-prntrop      =0.0001     # (m)
stats-clkstab      =5e-12      # (s/s)
ant1-postype       =llh        # (0:llh,1:xyz,2:single,3:posfile,4:rinexhead,5:rtcm)
ant1-pos1          =0          # (deg|m)
ant1-pos2          =0          # (deg|m)
ant1-pos3          =0          # (m|m)
ant1-anttype       =
ant1-antdele       =0          # (m)
ant1-antdeln       =0          # (m)
ant1-antdelu       =0          # (m)
ant2-postype       =llh        # (0:llh,1:xyz,2:single,3:posfile,4:rinexhead,5:rtcm)
ant2-pos1          =36.5613126674
ant2-pos2          =136.6140955143
ant2-pos3          =62.4724333119
ant2-anttype       =
ant2-antdele       =0          # (m)
ant2-antdeln       =0          # (m)
ant2-antdelu       =0          # (m)
misc-timeinterp    =off        # (0:off,1:on)
misc-sbasatsel     =0          # (0:all)
file-satantfile    =../../../data/igs08.atx
file-rcvantfile    =../../../data/igs08.atx
file-staposfile    =../../../data/station.pos
file-geoidfile     =
file-dcbfile       =../../../data/P1P2_ALL.DCB
file-tempdir       =../../../data/temp
file-geexefile     =
file-solstatfile   =
file-tracefile     =
```
実行は`RTKLIB/app/rtkrsv/gcc `で
` rtkrcv -o ../myrtkbl.conf -s `
画面に">"が表れるので、` status 1 `と入力すると、1秒間隔に情報を出力してくれます。
注目すべきは"solution status"。これがsingleからfloatを経て、fixになれば成功。その時、pos xyz fixed (m) rover　に補正された位置情報が入ります。fix時のステータス画面イメージを貼り付けておきます。

![FIX snapshot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1255232/886bde38-9ac0-05a9-7dc0-0eb7f657959b.png)

なお、XX_sol.posというファイルができているはずなので、これをPCに転送し、PC版RTKLIBのrtkplotで軌跡を見ることが出来ます。