LoRaのGateway Protocolについて

#はじめに
趣味でLoraのGateway（GW)やEndDevice（Node)とか作っています．
その中で色々と調べたことをまとめておこうと思います．

#Gateway Protocol とは
GatewayとNetwork Server（正確には窓口となるrouter）の間をインターネットでやり取りする部分のプロトコルです．
２つあり、一つはSemtechが策定したSemtech UDP protocolと呼ばれるものです．
二つめはTheThingsNetwork（TTN)が策定したGateway connector protocolです．
ここでは前者のSemtech UDP protocolについて書きます．

#基本的な考え方
通信はUpstream/DownstreamともにGateway側から始めます．
大抵はNAT/Firewallを超える必要がありますし、Network Serverの負荷を最小限にできるのかなと思います．
各データグラム（ネット上のパケット）は2バイトのトークンで識別されます．トークンは往信ではランダムな値で、返信は同じ値としています．
なお以下の説明ではGatewayから見たやりとりです．

#Upstream
##往路
PUSH_DATA　パケットを送信します．
フォーマットは次のようです．

| Bytes | 0 | 1-2 | 3 | 4-11 | 12-end |
|:-:|:-:|:-:|:-:|:-:|:-:|
|   | VERSION | random token | PUSH_DATA_ID | GW_ID | Payload |

ここでVERSION=0x02,PUSH_DATA_ID=0x00,GW_ID=MAC_addressです．
PayloadはJSONに似た形式で．内容が規定されれています．

##復路
PUSH_ACK　パケットが返ってきます．
フォーマットは次のようです．

| Bytes | 0 | 1-2 | 3 |
|:-:|:-:|:-:|:-:|
|   | VERSION | 受信したtoken | PUSH_ACK_ID |

ここでPUSH_DATA_ID=0x01です．

##JSON的形式のPayload
階層形式になっており、最上位（ルート）は受信したパケット情報を送る"rxpk" かGateway自身の情報を送る"stat"です.
どちらか一つでも両方を連続して送ることもできます．
その下に最低一つの内容を含むことになっており、それぞれ以下のものが規定されています．

###rxpk
１５項目が規定されていますがここではLORAの場合の項目を記します．

| 名前 | 内容 |
|:-:|:-:|
| time | 受信したUTC時刻 |
| tmss | 受信がGPS時刻 |
| tmst | 受信が完了した内部時刻 |
| chan | 無線部のIFチャネル  |
| rfch | 無線部のRFチャネル  |
| freq | 受信周波数  |
| stat | CRCの値、1=OK,-1=NG,0=noCRC |
| modu | 変調形式、LORAかFSK |
| datr | LoRaではデータレートID、SF7BW125など |
| codr | 誤り訂正レート、4/5など |
| lsnr | SNR信号雑音比 |
| rssi | 信号強度 |
| size | 受信したペイロードの長さ |
| data | 受信したペイロード |

ちなみにSingle_Chan_Packet_Forwarderではtime、tmssは送信していませんでした．
chanとrfchの違いがよく分かりませんがご存じの方は教えてください．

一例としてうちのGWのログから抜粋したものを示します．

```JSON:rxpk
 {"rxpk":[{"tmst":52224633,"chan":0,"rfch":0,"freq":923.400000,"stat":1,"modu":"LORA","datr":"SF7BW125","codr":"4/5","lsnr":9,"rssi":-75,"size":24,"data":"QIgiBCYANwAB1b5iqBO3034LpwEwsMfO"}]}
```
dataは受信したデータです．
このdataはAESで暗号化されていますのでなんだかわかりませんがここではcayenneLPP形式のGPSデータで11byteです．ヘッダーや誤り検出コードがついてサイズは24byteになります．暗号化は16byte単位なのでPadが追加されて32文字になっています．

サーバに送るべき2つ以上の受信パケットがある場合は内容を連続して送ることもできます（ ~~SF10-12など低速の場合は送信時間制限に注意ですが~~ 【2020-03-08】送信時間に注意するのは無線区間ですね．．勘違いしてました．）

##stat

| 名前 | 内容 |
|:-:|:-:|
| time | UTCシステム時刻  |
| lati | GWの緯度 |
| long | GWの経度 |
| alti | GWの高度 |
| rxnb | 無線受信したパケット数 |
| rxok | 無線受信しCRCがOKのパケット数 |
| rxfw | 無線受信しサーバに転送したパケット数 |
| ackr | サーバにUpしACKを受け取ったパーセント |
| dwnb | サーバからDownしたデータグラムの数 |
| txnb | 無線送信したパケット数 |
| pfrm | Gatewayのプラットフォーム |
| mail | Gatewayのオーナーの連絡先 |
| desc | Gatewayの説明 |

最後の３つはPROTOCOL.TXTには記載がありませんがSingle_Chan_Packet_Fowarderでは拡張されているのか送信しているので記載しました．TTNでは同様の内容をコンソールで設定するのでサーバによっては無くても良いのかも知れません．
同じく、うちのGWのログから抜粋したものを示します．（※一部文字、数字を入れ替えています．実際は半角です．）

```JSON:stat
{"stat":{"time":"2020-02-04 10:07:22 GMT","lati":35.*****,"long":136.xxxxx,"alti":6,"rxnb":0,"rxok":0,"rxfw":0,"ackr":0.0,"dwnb":0,"txnb":0,"pfrm":"Bidirection 1ch Gateway","mail":"tsuyoshi．ohashi＠ｇｍａｉｌ．ｃｏｍ","desc":"Raspberry pi and Dragino GPS-HAT"}}
```

stat単独で送ることもrxpkと連続して送ることもできます．

#Downstream
##往路
往路は「データありませんか？」的な問い合わせで始まります．
フォーマットは次のようです．

| Bytes | 0 | 1-2 | 3 | 4-11 |
|:-:|:-:|:-:|:-:|:-:|
|   | VERSION | random token | PULL_DATA_ID | GW_ID |

ここでPULL_DATA_ID=0x02です．
DownstreamのデータはGateway宛ではなく、EndDevice宛ですのでEndDeviceの近くにいると判断されればデータ配送が割り当てられるのだと思います．
EndDeviceのログを見ていると、知らない（失礼！）Node宛のデータが時々飛んできます．
【2020-02-06追記】
EndDeviceでデータを受け取れないことがあったのですがGWがPULL_DATAを送信していなかったのが原因でした．．．


##復路（データがある場合）
PULL_RESP　パケットが返ってきます．
フォーマットは次のようです．

| Bytes | 0 | 1-2 | 3 | 4-end |
|:-:|:-:|:-:|:-:|:-:|
|   | VERSION | random token | PULL_RESP_ID | Payload |
 
ここでPULL_RESP_ID=0x03です．
復路ですがランダムトークンが発行されているのはPULL_RESPに対して後でTX_ACKを返信するためです．
PayloadはUpstreamと同様にJSON的な形式です．

``` json
{
	"txpk": {...}
}
```
###txpk
１６項目が規定されていますがここではTTNからうちのGatewayに送信されてくる１３項目を記します．

| 名前 | 内容 |
|:-:|:-:|
| imme | 即時転送するか否か |
| tmst | 指定時刻で転送する |
| tmms | GPS時刻で転送する |
| freq | 送信周波数  |
| rfch | 無線部の送信チャネル |
| powe | 送信電力 |
| modu | 変調形式、LORAかFSK |
| datr | LoRaではデータレートID、SF7BW125など |
| codr | 誤り訂正レート、4/5など |
| ipol | Loraの変調極性 |
| size | 転送するペイロードの長さ |
| ncrc | CRCつけない |
| data | 転送するペイロード |

EndDeviceがClassBだと即時送信になるのかな？(推測、未確認）
【2019-02-16追記】immeに対応するのはClass Cのようです．
同じく、tmstはClass A、tmmsはClass Bに対応．


一例としてTheThingsNetworkに接続したうちのGWのログから抜粋したものを示します．

```JSON:txpk
 {"txpk":{"imme":false,"tmst":1618204691,"freq":923.4,"rfch":0,"powe":11,"modu":"LORA","datr":"SF7BW125","codr":"4/5","ipol":true,"size":18,"ncrc":true,"data":"oMwpBCYAAAABQ7mXXAIpyeBX"}}
```
dataはBase64でエンコードされています．ちなみに原文は「Qiita]の文字列です．
ここでは送信電力が11dBmと指定されていますがうちのGWでは反映してません．転送時刻も指定されていますがRX1のタイミングで転送しているので無視していることになる．．

###TX_ACK
サーバからデータを受け取ったらTX_ACKを返信します、
TX_ACKのフォーマットは次のようです．

| Bytes | 0 | 1-2 | 3 | 4-11 | 12-end |
|:-:|:-:|:-:|:-:|:-:|:-:|
|   | VERSION | PULL_RESPと同じtoken | TX_ACK_ID | GW_ID| Payload(Option) |

ここでTX_ACK_ID=0x05です．
EndDeviceに送信できないなどのエラーがあればネットワークサーバにルートの名前を「txpk_ack」として情報を送ることもできますがここでは省略します．

##復路（データがない場合）
PULL_ACKが返ってきます．
フォーマットは次のようです．

| Bytes | 0 | 1-2 | 3 |
|:-:|:-:|:-:|:-:|
|   | VERSION | PULL_DATAと同じtoken | PULL_ACK_ID |

ここでPULL_ACK_ID=0x04です．

#最後に
趣味でGatewatyやEndDeviceをraspberryPiで作っています．
やっとOTAAができるようになりました．
このまとめたのを元にGatewayの機能をアップグレードさせたいです．

#参考
https://github.com/Lora-net/packet_forwarder/blob/master/PROTOCOL.TXT

https://www.thethingsnetwork.org/docs/gateways/start/connection.html