Phantom4RTKのPPK処理

Phantom4RTKで撮影したデータをPPK(Post Processing Kinematic)するためのメモです。

処理の概要

  • Phantom4RTKではGNSSの2周波の観測データを取得できます。対応しているGNSSは、GPS(米)、GLONASS(露)、BeiDou(中)、Galileo(欧)です(みちびき(日)は未対応)。
  • 観測データおよび航行データは、0.2秒間隔(5Hz)で取得して、RTCM3.2 MSM5 形式のファイル「PPKRAW.bin」に保存されます。また、観測データをRINEX形式に変換したファイル「Rinex.obs」も保存されます。(PPKRAW.binを、FormatにRTCM3を指定しrtkconvで変換すると、Rinex.obsと同様の観測データのほかに、航行データが取得できます。)
  • また、各画像の撮影時刻と、観測データを受信したアンテナの位置と画像を撮影したCMOSセンサーの位置の差は「Timestamp.MRK」に保存されます。
  • 観測データをRTKLIBで処理することで正確な位置を算出し、CMOSセンサーとの位置の差を考慮して撮影画像に正確な位置を付与します。
  • その際、観測データの位置情報は0.2秒間隔で、撮影位置とは一致していないので、観測データの取得時刻と撮影時刻の関係から線形補間します。

RTKLIBで正確な位置を算出

移動局(Rover)のデータにはRinex.obsを使用し、基準局(Base)と航行データ(.nav)は、近くの電子基準点のデータをダウンロードして使用します。RTKLIBの処理の詳細は、以下を参照してください。

ポイントとしては、次の処理のためにRTKLIBののOptionsのOutputの設定でHeaderをOFFにして、Time formatをww ssss GPST(1980年1月6日からの週数と週始めからの秒数)にして、Field separatorを「,」にしておきます。また、HeightはGeodeticにして出力を標高値にしておきます。(こうすることで、後述のMetashapeで処理した後のdemデータが、標高値になります(未実施なので要確認)。←おそらく問題ないと思いますが、楕円体高で処理した場合とで精度に違いが出ないか少し気になってます。)

Phantom 4 RTK - PPK Processing Workflow | Drone Data Processing
https://swest.toppers.jp/SWEST21/program/pdfs/s5a_public.pdf
https://www.naro.affrc.go.jp/publicity_report/publication/files/drone_gnss.pdf

撮影位置を補間

RTKLIBの処理で作成した.posファイル(0.2秒間隔の位置情報)と撮影時刻を記録した「Timestamp.MRK」から撮影位置を算出します。
算出には、以下からダウンロードできるEXCELファイルを利用します。
使用方法は、以下のサイトとファイルに書いてある通りですが、position fileの貼り付けは、別のシートで.posファイルをカンマ区切りで読み込んでおいて貼り付けると簡単です。(数式の列番号が間違っているところがあるので、自分で修正が必要です。)

Phantom 4 RTK - PPK Processing Workflow | Drone Data Processing

撮影位置を画像に付与

EXCELから出力されたcsvファイル(画像のファイル名と位置情報の対応情報)をMetashapeでインポートすれば、画像に位置情報を付与できます。
以下の資料にもあるように解の精度(Fix,Float,Single)によって位置精度を変更するように改造すると良いかもしれません。(以下資料のTimestamp Converterは公開されていない?ようです)

以下の12ページ目参照
Phantom4 RTKで、後処理キネマティック(PPK)をする方法


精度の目安は、fixで水平0.03m 垂直0.05m floatで水平、垂直0.5mにしようと思います。あと、画像がサブフォルダに分かれていて同じファイル名が複数あるとcsvと対応づけできないので、Metashapeに読み込む前にファイル名をフォルダ名+ファイル名に変換しておきます。

参考バッチファイル
フォルダ名をファイル名の頭に追加するバッチ| OKWAVE

RTKGPS+(カスタムバージョン)の更新

以前ビルドしたものがpixel3で起動できなくなってたので修正しました。

修正したapkはこちらからダウンロードできます。

コードはこちら
github.com

以下作業メモ

以前修正したコードを取ってくる。
git clone -b Customized https://github.com/tmizu23/RtkGps.git

RTKLIBの取得(最新版に変更)

cd RtkGps
git submodule deinit -f jni/RTKLIB
git rm -rf jni/RTKLIB
rmdir /s .git\modules\jni\RTKLIB
git submodule add -b rtklib_2.4.3 https://github.com/tomojitakasu/RTKLIB.git jni/RTKLIB

jni/RTKLIB-unixsocket.patchのパッチをあてる
android studioVCS -> Apply patch... でファイルを指定する

以前の記事に従いソースを修正

simonlynen_android_libsの取得

git submodule update -i

build.gradle

コメントアウト
//ndkVersion '21.3.6528147'

変更
classpath 'com.android.tools.build:gradle:4.0.1'

local.properties

追加
ndk.dir=C\:\\Users\\mizutani\\AppData\\Local\\Android\\Sdk\\ndk\\21.3.6528147

以下のメッセージに従う

Minimum supported Gradle version is 6.1.1. Current version is 4.4.

Please fix the project's Gradle settings.
Fix Gradle wrapper and re-import project

AndroidManifest.xml

削除
android:minSdkVersion="18"
android:targetSdkVersion="18"

基盤地図情報 標高DEMデータ変換ツールのコンパイル方法

基盤地図情報 標高DEMデータ変換ツール Version1.7.0コンパイル方法です。
(QGIS3.10以降だと、以前のバージョンで変換したdemのCRSが不明となってしまったので、gdalとprojを更新して対応しました。ついでに32bit OS での動作は非対応にしました。その際の自分用メモです。

1. Visual Studio Community 2017をインストール

https://visualstudio.microsoft.com/ja/vs/older-downloads/
ここからダウンロードしてインストールして、x64 Native Tools コマンドプロンプトを起動します。

3.gdalをコンパイル

gdalの3.0.4をダウンロードします。
https://github.com/OSGeo/gdal/releases/download/v3.0.4/gdal-3.0.4.tar.gz

nmake.optを変更します。
・nmake.optのSETARGVを変更。
SETARGV = "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\lib\x64\setargv.obj"

・nmake.optのPROJを変更
PROJ_INCLUDE = -IC:\Users\mizutani\Desktop\proj-6.3.2\build\distro\include
PROJ_LIBRARY = "C:\Users\mizutani\Desktop\proj-6.3.2\build\distro\lib\proj.lib"

・nmake.optのssqlite3を変更
SQLITE_INC=-IC:\Users\mizutani\Desktop\proj-6.3.2\build\sqlite3
SQLITE_LIB=C:\Users\mizutani\Desktop\proj-6.3.2\build\sqlite3\sqlite3.lib

gdalを以下のコマンドでビルドします。
nmake -f makefile.vc MSVC_VER=1910 WIN64=yes
nmake -f makefile.vc devinstall MSVC_VER=1910 WIN64=yes

c:\warmerda 以下にgdal一式がインストールされます。

4. dem.cppをコンパイル

基盤地図情報 標高DEMデータ変換ツールの中に移動して以下を実行します。
cl -Ic:\warmerda\bld\include c:\warmerda\bld\lib\gdal_i.lib dem.cpp

5. gdalのexe、dll、dataをコピー

C:\warmerda\bld\binから基盤地図情報 標高DEMデータ変換ツールの中にコピーします。
・gdal300.dll
・gdalbuildvrt.exe
・gdalwarp.exe
・gdaldem.exe

C:\warmerda\bld\dataフォルダを基盤地図情報 標高DEMデータ変換ツールの中にコピーします。

6. projをコピー

C:\Users\mizutani\Desktop\proj-6.3.2\build\distro\share\projフォルダを基盤地図情報 標高DEMデータ変換ツールの中にコピーします。

以上です。

補足

変換結合.vbsの中でgdalを使用するための 環境変数を設定してあります。

tempEnv.Item("GDAL_DATA") = "data"
tempEnv.Item("GDAL_FILENAME_IS_UTF8") = "YES"
tempEnv.Item("PROJ_LIB") = "proj"   

Rを用いたGIS

しばらく前に作った資料「Rを用いたGIS」のリンクです。
google slideとgithub.ioに置いてあったけど、埋もれて探せなくなってきたのでこちらにスクラップ)

※資料を作成したときからsfパッケージのバージョンも上がってるので、今となっては動かないコードもあるかもしれません。

最初のバージョン
docs.google.com

応用編を一部変更
docs.google.com

上の資料のコードをRmarkdownで出力したもの
tmizu23.github.io

植生調査の凡例一覧をRSelemiumとrvestでスクレイピングしてxlsxデータにする

最新の凡例一覧をデータで公開してもらえれば済む話ですが、凡例Q&Aここのエクセルは更新されていないようなので、統一凡例一覧表スクレイピングしてみます。

ただ、凡例のサイトは生データのtableでなくて、javascriptで生成されており、rvestだけではスクレイピングできないのでSelemiumも利用します。

スクレイピングした凡例も、公開されているshpの属性中の凡例をすべては網羅していないのでご注意ください。

Selemiumの準備

以下のサイトを参考にしました。C:\seleniumに必要なファイルを入れてパスも通しておきます。javaが入っていない場合は、javaもインストールしておきます。
qiita.com

Rでスクレイピングする準備

RでRSeleniumとrvestとtydiverseのパッケージをインストールしておきます。

スクレイピングの実行

seleniumのサーバーを起動します。コマンドプロンプト

cd C:\
java -jar selenium-server-standalone-3.141.59.jar

以下のコードをRで実行すると、凡例一覧をエクセルで保存できます。10ページ目が空になっているので、そこだけ後から修正してください。

gist.github.com


RTKGPS+をWindowsでビルド

(2020.8.12追記 apkを更新しました。http://tmizu23.hatenablog.com/entry/2020/08/09/100146 )

スマホ用のRTKアプリ「RTKGPS+」をカスタマイズしてビルドする方法です。

GNSSの後処理キネマティック (PPK) をするためには、受信機のRawデータを取得する必要があります。Pixcel3などのアンドロイドスマホとNeo-M8TなどのRTK対応の受信機をUSBで接続してRTKGPS+を使うと、Rawデータを簡単に取得できます。ただ、オリジナルのアプリだとQZSSのデータが表示されないのと、2周波対応のZED-F9Pの受信機に対応してなさそうなので、カスタマイズしてみます。ついでにマップに地理院地図も表示できるようにしてみます。

※オリジナルのものでもQZSSのRawデータは取得できてるっぽいのでPPKの処理は問題ないですが。

2周波表示とQZSS表示のポイントは、該当箇所のコメントアウトを外すことと、RTKGPS+用のパッチを当てればよかっただけなのですが...コードを理解してそれに気が付くまでが長かった...

f:id:tmizu23:20191107194012j:plain:w300


play.google.com

Android Studioのインストール

developer.android.com

ダウンロードしてインストールする

RTKGPS+のコード取得

github.com

git で以下のコマンドで取得する。RTKLIBなど関連プロジェクトも含めて取得するため --recursive が必要なので注意!

git clone --recursive https://github.com/eltorio/RtkGps.git

Android StudioでRTKGPS+を開く

Android Studioで取得したRtkGpsフォルダを開くと、自動でSyncされて足りないライブラリなどをインストールするようにメッセージやらエラーがでるので従う。

必要なライブラリがそろうと、ビルドできるようになるがエラーで止まるので、以下のコードの修正をする。

環境変数を設定する

Windows環境変数のPathに以下を追加
C:\Users\mizutani\AppData\Local\Android\Sdk\ndk\20.0.559457
C:\Users\mizutani\AppData\Local\Android\Sdk\platform-tools

ビルドが通るようにコードの修正

  • jni/Application.mkを修正

APP_STLc++_staticから変更。APP_ABIを追加

APP_STL := c++_shared
APP_ABI := armeabi-v7a
  • build.gradleを修正

abiFiltersをarmeabi-v7a以外をコメントアウトする。(とりあえずデバッグするためにビルドを速くしたいから。リリース時は、要変更)
defaultConfigの packagingOptionsに pickFirstを追加する。

defaultConfig {
        applicationId "gpsplus.rtkgps"
        minSdkVersion 21
        targetSdkVersion 26
        multiDexEnabled true
  ndk {
            abiFilters = []
            abiFilters.add('armeabi-v7a')
            //abiFilters.add('x86')
            //abiFilters.add('x86_64')
            //abiFilters.add('arm64-v8a')
        }
        packagingOptions {
            pickFirst 'lib/armeabi-v7a/libc++_shared.so'
            pickFirst 'lib/armeabi-v7a/libproj.so'
            pickFirst 'lib/armeabi-v7a/librtkgps.so'
            pickFirst 'lib/armeabi-v7a/libntripcaster.so'
            pickFirst 'lib/armeabi-v7a/libgdalalljni.so'
            pickFirst 'lib/x86_64/libc++_shared.so'
            //pickFirst 'lib/x86_64/libntripcaster.so'
            //pickFirst 'lib/x86_64/librtkgps.so'
            //pickFirst 'lib/x86_64/libproj.so'
            //pickFirst 'lib/x86_64/libgdalalljni.so'
            //pickFirst 'lib/x86/libc++_shared.so'
            //pickFirst 'lib/x86/librtkgps.so'
            //pickFirst 'lib/x86/libgdalalljni.so'
            //pickFirst 'lib/x86/libntripcaster.so'
            //pickFirst 'lib/x86/libproj.so'
            //pickFirst 'lib/arm64-v8a/libc++_shared.so'
            //pickFirst 'lib/arm64-v8a/libproj.so'
            //pickFirst 'lib/arm64-v8a/librtkgps.so'
            //pickFirst 'lib/arm64-v8a/libntripcaster.so'
            //pickFirst 'lib/arm64-v8a/libgdalalljni.so'
        }
 もともとあるコード
}
  • License.java.sampleの変更

src/gpsplus/rtkgps/geoportail/License.java.sampleのファイル名をLicense.javaに変更

QZSSを表示できるようにコードを修正

jni/rtklib.mkのRTKLIB_CFLAGSをコメントアウトしていあるものに変更(RtkGps+の作者はQZSSが関係ない地域の人だったのかな?)

RTKLIB_CFLAGS := -DENAGLO -DENAGAL -DENAQZS -DENACMP -DNFREQ=3 -DTRACE

ライブラリのビルド

Android StudioのTerminalでRtkGps/jniのフォルダに移動後、ndk-buildのコマンドでライブラリをビルド

cd RtkGps/jni
ndk-build

アプリのビルド

Android Studioでビルドボタンを押す

アプリの起動テスト

Android StudioでBuild APKをするとbuild/outputs/debugフォルダにapkができるので、実際のスマホにコピーして、ファイルアプリから開いてインストールする。

デバッグ

https://okfoxy1990.com/android-apps-wifi-debug
Android Studioで開発したアプリを実機でテストする方法【初心者向け】 | TechAcademyマガジン

上記を参考にしてWIFI接続でデバッグできるようにする。USBは受信機を接続しているので。

開発者モードでUSBデバッグを有効にしてから、USB接続して、以下のコマンドを打つ
adb.exe tcpip 5555
USB外して、以下のコマンドを打つ。ipはスマホのipアドレスを確認して指定
adb.exe connect 192.168.1.11

Android Studioで実機のデバイスを指定して実行すると、実機にアプリがインストールされて起動する。

デバッグモードとなっているのでAndroid StudioのLogcatタブにLogが出力される。

RTKLIBの更新

submoduleとなっているRTKLIBを更新する。

rtklibexplorerバージョンの場合
cd RtkGps
git submodule deinit  -f jni/RTKLIB
git rm -rf jni/RTKLIB
rmdir /s .git\modules\jni\RTKLIB
git submodule add https://github.com/rtklibexplorer/RTKLIB.git jni/RTKLIB

jni/rtklib.mkの以下の行を削除

    $(RTKLIB_PATH)/src/rcv/ss2.c \

jni/RTKLIB-unixsocket.patchのパッチをあてる
android studioVCS -> Apply patch... でファイルを指定する

オリジナルの場合

android studioのterminalで以下を実行(コマンドプロンプトなのでフォルダ消すときはrmdir)

cd RtkGps
git submodule deinit  -f jni/RTKLIB
git rm -rf jni/RTKLIB
rmdir /s .git\modules\jni\RTKLIB
git submodule add -b rtklib_2.4.3 https://github.com/tomojitakasu/RTKLIB.git jni/RTKLIB

jni/rtklib.mkの以下の行を削除

$(RTKLIB_PATH)/src/rcv/swiftnav.c \
$(RTKLIB_PATH)/src/rcv/comnav.c \

jni/prcopt.cを変更する(面倒なのでパッチの内容)

diff --git a/jni/prcopt.c b/jni/prcopt.c
index 49d6884..7b88048 100644
--- a/jni/prcopt.c
+++ b/jni/prcopt.c
@@ -189,16 +189,16 @@ void processing_options2prcopt_t(JNIEnv* env, jobject thiz, prcopt_t *dst)
    GET_FIELD(modear, Int)
    GET_FIELD(glomodear, Int)
 
-   GET_FIELD(gpsmodear, Int)
+   //GET_FIELD(gpsmodear, Int)
    GET_FIELD(bdsmodear, Int)
-   GET_FIELD(arfilter, Int)
-   GET_FIELD(minfixsats, Int)
-   GET_FIELD(minholdsats, Int)
-   GET_FIELD(mindropsats, Int)
-   GET_FIELD(rcvstds, Int)
+   //GET_FIELD(arfilter, Int)
+   //GET_FIELD(minfixsats, Int)
+   //GET_FIELD(minholdsats, Int)
+   //GET_FIELD(mindropsats, Int)
+   //GET_FIELD(rcvstds, Int)
    GET_FIELD(armaxiter, Int)
-   GET_FIELD(varholdamb, Double)
-   GET_FIELD(gainholdamb, Double)
+   //GET_FIELD(varholdamb, Double)
+   //GET_FIELD(gainholdamb, Double)
    GET_FIELD(maxaveep, Int)
    GET_FIELD(initrst, Int)
    GET_FIELD(outsingle, Int)
@@ -352,22 +352,21 @@ static void ProcessingOptions_load_defaults(JNIEnv* env, jobject thiz)
    SET_FIELD(modear, Int)
    SET_FIELD(glomodear, Int)
 
-   SET_FIELD(gpsmodear, Int)
+   //SET_FIELD(gpsmodear, Int)
    SET_FIELD(bdsmodear, Int)
-   SET_FIELD(arfilter, Int)
-   SET_FIELD(minfixsats, Int)
-   SET_FIELD(minholdsats, Int)
-   SET_FIELD(mindropsats, Int)
-   SET_FIELD(rcvstds, Int)
+   //SET_FIELD(arfilter, Int)
+   //SET_FIELD(minfixsats, Int)
+   //SET_FIELD(minholdsats, Int)
+   //SET_FIELD(mindropsats, Int)
+   //SET_FIELD(rcvstds, Int)
    SET_FIELD(armaxiter, Int)
-   SET_FIELD(varholdamb, Double)
-   SET_FIELD(gainholdamb, Double)
-   SET_FIELD(maxaveep, Int)
-   SET_FIELD(initrst, Int)
-   SET_FIELD(outsingle, Int)
-   SET_FIELD(syncsol, Int)
-   SET_FIELD(freqopt, Int)
-
+   //SET_FIELD(varholdamb, Double)
+   //SET_FIELD(gainholdamb, Double)
+   //SET_FIELD(maxaveep, Int)
+   //SET_FIELD(initrst, Int)
+   //SET_FIELD(outsingle, Int)
+   //SET_FIELD(syncsol, Int)
+   //SET_FIELD(freqopt, Int)
    SET_FIELD(maxout, Int)
    SET_FIELD(minlock, Int)
    SET_FIELD(minfix, Int)

jni/RTKLIB-unixsocket.patchのパッチをあてる
android studioVCS -> Apply patch... でファイルを指定する

osmdroidの更新と地理院地図の追加

build.gradleにosmdroidの最新バージョン追加。もともとのosmdroidのjarは削除する

dependencies {
    implementation 'org.osmdroid:osmdroid-android:6.1.2'
もともとあったコード
}

osmdroidのバージョンの違いによりエラーがでるのでMapFragment.javaまわりを修正。地理院地図を追加してgeoportailの地図を削除。

上記を変更したRtkGps+のコードはこちらにあります。
(2019.11.29 追記:apkも以下のreleaseの中にアップしておきました。)
github.com

あとがき

もともとの目的はドローン用対空標識の座標決めで、2周波RTKだったら3分ぐらいの受信と電子基準点との後処理でFixするかも?という期待でしたが、結果5km離れた電子基準点では、3分ではFixせず、最低5分は必要でした。あと、PPKしか試してないので、RTKGps+を使って2周波のRTK測位を試したいですが、Zed-F9Pは1台しかないので、安くなってからかな...