RTKをUnityでやってみた

## RTKってこんな感じ

![Videotogif (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76572/2e8c5500-7381-a0ce-a0ff-ae7b16b857e6.gif)

動画ではいい感じのところを切り取っていますが、中央に戻ってくる箇所が結構ずれることがあります。
それでも10cmくらいのずれ（体感）っぽいので、GPSと比べると精度高い

## RTK（Real Time Kinematic）とは

ざっくり言えば、高精度になったGPS！

色々な人が解説しているので、ここでは省略
https://www.softbank.jp/biz/blog/business/articles/202009/rtk-gps/
https://www.akasakatec.com/blog/4138/
https://atcl-dsj.com/work/2685/
https://qiita.com/m_take/items/f416b62fb6edfb7a8c4d

自分なりに簡略化しつつ、用語整理もかねて図を作ってみた

[![Image from Gyazo](https://i.gyazo.com/ee1a875c98684a86be32effc3ad83260.png)](https://gyazo.com/ee1a875c98684a86be32effc3ad83260)

この他にも```NTRIP```って用語とかがあります。

今回はこの中の「Client」部分のみです。
「Caster」は有志の方からもらってます。
https://rtk.silentsystem.jp/

「Server」部分だったり、「Caster」のrtk2go.comを試してみたり、ローカルで構築してみたりってのもやっていますので、いずれ機会があればまた記事書きます。

### RTKの用途
* ドローン
* 自動運転
* 測量
とからしい

## Unityでやってみた

### 買ったやつ

https://www.switch-science.com/products/8288?_pos=3&_sid=3e7756d81

usbケーブルでPCと繋いで、シリアル通信でやりとりできます！すばらしい

### 大枠の流れ
1. NTRIPでRTCMデータ受け取り
    1. Unity的に、```TcpClient``` 作って、```GetStream();```から```StreamReader``` 作って、受け取り
    1. バイナリー形式で受け取ったらうまくいった
1. （今回は）シリアル通信で、手元のRTCMデバイスへ書き込み
    1. Unity的に、```SerialPort``` 作って、```Write();```で書き込み
1. シリアル通信で、読み込み
    1. Unity的に、```ReadLine();```で読み込み


### ソースコード一部

NTRIPの通信準備

```C#
client = new TcpClient();
await client.ConnectAsync(host, port);

networkStream = client.GetStream();
reader = new StreamReader(networkStream);
writer = new StreamWriter(networkStream);

await writer.WriteLineAsync("GET /" + mountpoint + " HTTP/1.1");
await writer.WriteLineAsync("Host " + host);
await writer.WriteLineAsync("User-Agent: NTRIP curl/0.0");
await writer.WriteLineAsync("Authorization: " + GetAuthenticate(username, password));
await writer.WriteLineAsync("");
await writer.FlushAsync();
```

スレッドとか用意して受け取り

```C#
byte[] buffer = new byte[1024];
int len = await reader.BaseStream.ReadAsync(buffer, 0, buffer.Length, cancellationTokenSource.Token);
```

シリアル通信準備して、

```C#
serialPort = new SerialPort(portName, baudRate, Parity.None, 8, StopBits.One);
```

書き込み
タイミングはUpdateとかじゃなく、NTRIP通信で受け取ったタイミングがいい

```C#
serialPort.Write(buffer, 0, buffer.Length);
```

スレッドとか用意して読み込み

```C#
message = serialPort.ReadLine();
```

NMEA形式のデータをいったんGGAだけのものをパースしてみた

```C#
public struct GGAData
{
    public string OriginalString;
    public string TimeString;
    public int Hours;
    public int Minits;
    public float Seconds;
    public double Latitude;
    public double Longitude;
    public int Quality;
}

public GGAData ggaData = new GGAData();

public void ParseRoverData(string data)
{
    if (data.Contains("GGA"))
    {
        ggaData.OriginalString = data;
        string[] dataArr = data.Split(',');
        ggaData.TimeString = dataArr[1];
        ggaData.Hours = int.Parse(ggaData.TimeString.Substring(0, 2)) + 9;
        ggaData.Minits = int.Parse(ggaData.TimeString.Substring(2, 2));
        ggaData.Seconds = float.Parse(ggaData.TimeString.Substring(4, 4));
        ggaData.Latitude = double.Parse(dataArr[2].Substring(2, dataArr[2].Length - 2));
        ggaData.Longitude = double.Parse(dataArr[4].Substring(3, dataArr[4].Length - 3));
        ggaData.Quality = int.Parse(dataArr[6]);
    }
}
```

プロジェクト全体は準備中です

## これつかってなにしようかな

* 床にプロジェクションして、なんかついてくるとか
    * プロジェクションするなら暗い部屋内とかがいいけど、GPSとかには不向きか
    * まだまだ機材が大きいので卓上くらいだと効果的じゃないか
* 本当の最短距離を測る
    * 徒歩でしか行けない道とか、道幅大きいとアウトインアウトで短くなるとかで、GoogleMapの最短距離の計算とはまた違った結果になるんじゃないかな
    * 毎回の保育園への送り迎えとかで測りたい！
        * でも冬は日が当たるほうを歩きたいっとかってのも思ったりしてる
* 鬼ごっこのお助けアイテム的な
    * FPSのゲームみたいな壁腰に相手が見えるとか
* 近距離でも正確なセカイカメラ