前回は、F9Pが出力したファイルから通常のRTKモードでNMEA $GNGGAセンテンスだけ、VBAで読み込んで欲しいデータをグラフ化するプログラムを作ったのですが、今回は、UBLOX社オリジナルのセンテンスを使うMovingBaseというRTKモードでの出力ファイルを読み込むVBAプログラムを作ってます。まずは、出力ファイルの中身のデータフォーマットを調べます。
「ExcelVBAでは初歩的なRTKデータの学習に使える程度のバイナリ変換機能しかありませんので、PCなら.NET VB、.NET C#,マイコンならC++でRTKバイナリーデータを変換することをお勧めします。初めの数か月はVBAでRTKに慣れてきたらVBAを卒業することでRTKが本格的にできるようになります。」
※2020年4月追記 NAV-PVT,NAV-RELPOSNEDをCSVにするTOOL公開しました
MovingBaseや基線解析で頻繁に使うPVTとRELPOSNEDをubxファイルからCSVファイルに変換してExcelで解析作業するためのTOOLを公開しました。スキー解析用アプリの初期設定部分が丁度このTOOLになっています。下記記事をご覧ください。
●GNSS関連のセンテンス規格に触れての感想
GNSSの業界は、元々が海洋関連業界がユーザーでそれに合わせて開発してきた経緯があってRTCM(RadioTechnical Comission For Maritime Service)、海運業界団体が独自の規格を作ってきたみたいです。RTKで基準局とローバーの通信仕様であるRTCM3センテンスの仕様は有償規格書で、数万円だして海外から購入する必要があります。
ですので、WEB上で詳細なRTCM3の解説資料はありません。
※後日 専門家に教えていただいたら国土地理院の資料に概要の説明がありました。https://www.gsi.go.jp/common/000068240.pdf このPDFの78ページあたりの記述があります。
しかし、UBLOXのF9Pは、NMEA,RTCM3規格をサポートしていてさらに、UBLOX社独自規格のUBXセンテンス群も独自に用意して対応させてます、MovingBaseもUBLOX社独自の規格です。UBLOX社の規格書を読めば、UBX以外でも、RTCM3も多少わかるので上記高価なRTCM3規格書を買わないでもプログラムは作れます。団体、規格としてWORLDWIDEで貢献したいなら、規格をWEBで無償公開したほうが普及が進むと思うのですが、RTCMという団体が不可思議です。将来的には、GAFAMとかが、新たなフィーチャーがはいったUBXセンテンスを作って普及させていくとUBXが世界標準になってしまうかもしれません。
●UBLOX F9Pのセンテンスの仕様書
「UBLOX ZED-F9P Interface Description 」pdfをダウンロードします。
https://www.u-blox.com/sites/default/files/u-blox_ZED-F9P_InterfaceDescription_%28UBX-18010854%29.pdf
●Ublox F9P Intergface Description でのRTCM3の解説
257ページから解説があります。
●MovingBaseモードでの出力センテンスは2個だけです。
NAV-PVTとNAV-RELPOSNEDだけです。
①NAV-PVT
上記interface descriptionの163ページにあります。 下記pdf
➁RELPOSNEDセンテンス
覚えにくい名称ですが、意味は、REL=相対的、POS=相対位置データ NED=North,East,Depthの3座標の相対距離を示すセンテンスです。
下記pdf
●EXCEL VBAで読み込み
F9PでMovingBase測定が終わってログファイルを見ます。
terapadなどテキストエディタでみるとバイナリで化け文字になります。
普段マイコンからのデータはASCII形式でCSVファイルになっているので数値を文字として一目で認識できるのですが、UBXファイルはバイナリです。
何故バイナリかというと、転送データ量が圧倒的に少なくてすむからです。
ASCIIで1バイトを表現する数値を送るには、0~255まで1~3文字3バイト必要ですが、バイナリーなら1バイトで0~255までの数値を表現できるからです。ASCIIファイルより半分以下に小さくなるのがバイナリーのメリットです。しかし、すぐみられないので、自分でアプリを探すか、プログラムを作らないといけないところがバイナリーファイルの面倒な点です。
●EXCEL VBAでバイナリファイルを読み込む
こちらにサンプルがありました。
https://qiita.com/yamashiroakihito/items/45d8368d9e62f27221fe
ここのサンプルVBAソースをそのままコピペさせていただいて動きました。感謝です。コピペしたテキスト
Sub Main() 'ファイル選択ダイアログでファイルを指定 Dim vFilePath As Variant vFilePath = Application.GetOpenFilename 'キャンセルボタンを押されたら、処理終了 If vFilePath = False Then End End If 'ファイルサイズが0バイトの場合も処理終了 Dim nFileLen As Long nFileLen = FileLen(vFilePath) If nFileLen = 0 Then End End If '空いているファイル番号を取得 Dim iFile As Integer iFile = FreeFile '指定されたファイルを取得したファイル番号としてバイナリモードで開く Open vFilePath For Binary As #iFile 'ファイルサイズ分のバイト配列を用意 Dim bData() As Byte ReDim bData(0 To nFileLen - 1) 'バイト配列に指定ファイルを展開 Get #iFile, , bData Close #iFile Dim i As Long 'ScreenUpdatingやらCalculationなどの高速化設定は割愛 'バイナリデータをダンプ表示します。 For i = 0 To nFileLen - 1 y = i \ 16 + 1 x = i Mod 16 + 1 Cells(y, x) = "'" & Right("0" & Hex(bData(i)), 2) Next End Sub |
■UBXのヘッダ順にセンテンス行を作る
上記サンプルプログラムにちょっと変更をいれます。
ヘッダ頭が0xB5ですので、ファイルから読み込んだバイナリー配列
bData()から0xB5を探して見つかったら次の0xB5が来るまで
同じ行(L行)にバイトデータを桁方向へ(K桁)でインクリメントしていきます。
今回は、私はHEX形式にしないでDECでセルに書き込みました。
=>.Cells(L, K) = bData(i)
HEX形式なら=>.Cells(L, K) = “‘” & Right(“0” & Hex(bData(i + j)), 2)
となります。
With Worksheets(“sheet1”) L = 1 K = 1 For i = 1 To nFileLen – 1 header = Right(“0” & Hex(bData(i)), 2) If header = “B5” Then L = L + 1 K = 1 End If ‘ .Cells(L, K) = “‘” & Right(“0” & Hex(bData(i + j)), 2) .Cells(L, K) = bData(i) K = K + 1 Next i Maxrow = Range(“A1”).End(xlDown).Row MaxCol = Range(“A1”).End(xlToRight).ColumnEnd With |
上記プログラムの結果
目が回ってしまうのですが、欲しいデータがどこにあるか見つけてそこだけ抽出すれば見えて来ます。上記HEXからNAV-PVTとNAV-RELPOSNEDのデータフレーム資料をみながら、探します。探した位置で4バイトを数値に4バイトの長整数に変換して、本当にそこでよいのかの確認作業がありますので、バイナリーファイルから欲しいデータを切りだす作業は1日がかりでした。
※2022年5月注記:本記事執筆から2年後、精度管理を強化してますが、高精度パラメータを組み入れた計算をしてないことに気づきました。RELPOSNEDのrelPosHPN,relPosHPE,relPosHPD,relPosHPLの4個は、0.1mm単位の高精度パラメータですが、8ビットの整数(-128から127)まで表現されますが、VBAではこの整数型がないので自分で型変換プログラムを作る必要があります。下記式です。
■RELPOSNEDバイト型配列のバイト型を符号付8ビット整数に変換する式です。
parRELP(8) = (bRELP(32 ) And 127) – (bRELP(32 ) And 128)
というビット操作で、符号なしByte型を符号付き8ビット整数に変換してあります。
教えていただいたサイト様に感謝
https://plaza.rakuten.co.jp/spectra/diary/201512090000/
●ポイント1:iTowという4Byteのタイムスタンプからmsec単位で時刻を得ます。
時刻msec=iTow1+iTow2*256+iTow3*256*256+iTow4*256*256*256
となります。
①PVTセンテンスのiTow
0XB5を0バイト番目とすると
iTow1は6番目から9番目までの4バイトで表現されます。
0 | 1 | 2 | 3 | 4 | 5 | 6iTOW | 7iTow | 8iTow | 9iTow |
B5 | 62 | 01 | 07 | 5C | 0 | B0 | 41 | 71 | 01 |
➁RELPOSNEDセンテンスのiTOW
10番目のバイトからiTowがでてきます。
0 | 1 | 2 | 3 | 4Ver | 5res | 6Ver | 7Rev | 8ID | 9ID | 10iTow | 11iTow | 12iTow | 13iTow |
DC | 62 | 01 | 3C | 40 | 00 | 01 | 00 | 00 | 0 | E8 | 40 | 71 | 01 |
ポイント2:欲しいデータだけ抽出
PVTは、NTRIPで補正されたRover測位結果をだしてくれるので、iTowとLongtitudeとLatitudeだけでいいです。
RELPOSNEDは、相対位置のデータですが
iTowとN位置とE位置とD位置とLengthとHeading角あればいいです。
14番目から25番目まで12バイトの位置です。
14relN | 15relN | 16relN | 17relN | 18relE | 19relE | 20relE | 21relE | 22relD | 23relD | 24relD | 25relD |
EB | FF | FF | FF | 4F | 00 | 00 | 00 | 02 | 00 | 00 | 00 |
LengthとHeadingは26番目~33番目まで8バイト位置です。
26LEN | 27LEN | 28LEN | 29LEN | 30HEAD | 31HEAD | 32HEAD | 33HEAD |
52 | 00 | 00 | 00 | 3A | 9E | A0 | 00 |
●これらを読み込み部分のVBAソース
上記順番で、0xB5をi番目で見つけたらi+オフセットで目的のバイトを
指定して4バイトの整数に変換します。しかし、VBAに型変換エラーがうるさいので、すべてDOUBLE型に変換しエラー除けしてます。この辺が
変数型宣言がグレーな処理をしているVBAの欠点だと感じました。
With Worksheets(“sheet1”) ‘PVT B5620107 ->RELPOSNED B562013C 順で出力される Maxrow = Range(“A1”).End(xlDown).Row MaxCol = Range(“A1”).End(xlToRight).ColumnFor i = 1 To Maxrow ‘nFileLen – 1 ‘ header = Right(“0” & Hex(bData(i)), 2) + Right(“0” & Hex(bData(i + 1)), 2) + Right(“0” & Hex(bData(i + 2)), 2) + Right(“0” & Hex(bData(i + 3)), 2) If .Cells(i, 4) = 7 Then ‘PVT L = L + 1 iTOW = CDbl(.Cells(i, 7)) + CDbl(.Cells(i, 8)) * 256 + CDbl(.Cells(i, 9)) * CDbl(65536) + CDbl(.Cells(i, 10)) * CDbl(16777216) Lon = CDbl(.Cells(i, 31)) + CDbl(.Cells(i, 32)) * 256 + CDbl(.Cells(i, 33)) * CDbl(65536) + CDbl(.Cells(i, 34)) * CDbl(16777216) Lat = CDbl(.Cells(i, 35)) + CDbl(.Cells(i, 36)) * 256 + CDbl(.Cells(i, 37)) * CDbl(65536) + CDbl(.Cells(i, 38)) * CDbl(16777216) Worksheets(“sheet2”).Cells(L, 1) = iTOW Worksheets(“sheet2”).Cells(L, 2) = Lon ‘ * 1.1 ‘cm Worksheets(“sheet2”).Cells(L, 3) = Lat ‘ * 1.1 ‘cmEnd IfIf .Cells(i, 4) = 60 Then ‘RELPOSNED relTow = CDbl(.Cells(i, 11)) + CDbl(.Cells(i, 12)) * 256 + CDbl(.Cells(i, 13)) * CDbl(65536) + CDbl(.Cells(i, 14)) * CDbl(16777216) relN = CDbl(.Cells(i, 15)) + CDbl(.Cells(i, 16)) * 256 + CDbl(.Cells(i, 17)) * CDbl(65536) + CDbl(.Cells(i, 18)) * CDbl(16777216) If .Cells(i, 18) >= 12 Then relN = relN – 4294967296# End If relE = CDbl(.Cells(i, 19)) + CDbl(.Cells(i, 20)) * 256 + CDbl(.Cells(i, 21)) * CDbl(65536) + CDbl(.Cells(i, 22)) * CDbl(16777216) If .Cells(i, 22) >= 128 Then relE = relE – 4294967296# End If relD = CDbl(.Cells(i, 23)) + CDbl(.Cells(i, 24)) * 256 + CDbl(.Cells(i, 25)) * CDbl(65536) + CDbl(.Cells(i, 26)) * CDbl(16777216) If .Cells(i, 26) >= 128 Then relD = relD – 4294967296# End If relLen = CDbl(.Cells(i, 27)) + CDbl(.Cells(i, 28)) * 256 + CDbl(.Cells(i, 29)) * CDbl(65536) + CDbl(.Cells(i, 30)) * CDbl(16777216) relHead = CDbl(.Cells(i, 31)) + CDbl(.Cells(i, 32)) * 256 + CDbl(.Cells(i, 33)) * CDbl(65536) + CDbl(.Cells(i, 34)) * CDbl(16777216)Worksheets(“sheet2”).Cells(L, 4) = relTow Worksheets(“sheet2”).Cells(L, 5) = relN ‘cm Worksheets(“sheet2”).Cells(L, 6) = relE ‘cm Worksheets(“sheet2”).Cells(L, 7) = relD ‘cm Worksheets(“sheet2”).Cells(L, 8) = relLen ‘cm Worksheets(“sheet2”).Cells(L, 9) = relHead / 100000 ‘degEnd If ‘Cells(L, K) = “‘” & Right(“0” & Hex(bData(i + j)), 2) ‘ K = K + 1Next iEnd With |
●上記プログラムから抽出したデータでMovingBaseの測定評価を始めてます。
RELPOSNEDの場合MBを原点として、N位置とE位置がでるので
どんなに動かしてもBASE-ROVERアンテナ間距離の描く円弧になります。NAV-PVTの緯度経度は通常のRTKプロットですがRoverの軌跡です。
●以後
実際のデータをみると結構エラーで没の行があって、それらを消去しないとまともなグラフができないのでVBAで異常データを消去するプログラムも必要です。最終的には、GNGGAと同様にMBモードもグラフプログラムにして、HEADING角度ベクトルを位置ポイント上に表示できるプログラムまで作ります。
※本プログラムは、初期レベルで、バグがあってさらに非常に遅い処理になっていて実用性がない(100kBで10分かかる)バグ対策と
VBAの高速化を図りました。数秒で処理できるようになりました。
ポイント0:ヘッダーのB5を検出するのはデータと間違うバグになるので、最初から172バイトを読み込んでしまう単純なデータ受信のほうが間違いがありませんでした。
ポイント1:セルへ逐次書き込まないで、配列変数でバッファしておくこと
ポイント2:セルへの書き込みは range コマンドを使ってrange () =配列変数名 で一挙に書きこむ
ポイント3:センテンスは、バイト数が決まっているので、ヘッダを検索しないでバイト数だけ一挙によむ
この高速化の記事はこちらです。