2026-08-02追記: 本書が対象としたTrack #36(P5-2)・Track #37(P5-2A)はいずれもoutcome=adoptedで完了し、Issue #21(P5)は技術検証ベースラインの確立をもってcloseした。C2以降(低リスク移動試験等、軌跡・基準線・横ずれ表示を前提とする段階)は、それらの機能がまだ実装されていないため現時点では実施できない。UI仕様策定を含む残りスコープはIssue #38「P6: トラクター耕耘アシストアプリ UI仕様策定とスパイラル開発」がスパイラル開発として引き継ぐ。本書はP5-2の技術検証記録として残し、C1までの内容はP6でも前提知識として参照する。
Issue #21「P5: トラクター耕耘アシストアプリ製品化」を、天空視界が狭い開発場所でも効率よく進めるための開発順序、試験境界、屋外受入ゲートを定めます。
開発場所ではRTKのFLOAT/FIXが安定しない可能性があります。開発と屋外試験を短い周期で往復せず、現場で取得した良質なデータを開発環境へ持ち帰り、同じ条件を何度でも再生できる構造を先に作ります。
製品アプリの標準RTK接続先は、本番基準局とします。
| 項目 | 値 |
|---|---|
| ホスト | 10.0.0.4 |
| ポート | 2101 |
| マウントポイント | RTK2B_BASE |
| NTRIP認証 | なし |
| GGA送信 | なし |
| Android WireGuardアドレス | 10.0.0.3 |
開発環境ではWi-Fiを下位ネットワークとして利用できます。圃場ではスマホBluetoothテザリングを利用します。どちらの場合も、基準局宛10.0.0.4はAndroidのWireGuard tun0を通します。
画面、軌跡、横ずれ計算より先に、次の一連の流れを自作アプリだけで成立させます。
自作アプリ
|
| NTRIP
v
本番基準局 10.0.0.4:2101/RTK2B_BASE
|
| RTCM3
v
自作アプリ
|
| Bluetooth SPP書き込み
v
DG-PRO1RWS
|
| 同じBluetooth接続から補正後NMEA
v
自作アプリ
|
v
測位品質判定・診断ログ
最初の縦切り完了条件:
ライブ通信と再生を交換できるよう、UIや製品ロジックから入出力を分離します。名称は実装時に変更できますが、責務の境界は維持します。
RtcmSource
├─ LiveNtripSource
└─ RecordedRtcmSource
GnssTransport
├─ BluetoothGnssTransport
└─ FakeGnssTransport
PositionSource
├─ LiveNmeaSource
└─ RecordedNmeaSource
DiagnosticRecorder
├─ RTCM3
├─ Bluetooth送受信
├─ NMEA
└─ 状態遷移・警告・エラー
必須の設計条件:
現場で原因を特定できなくても、開発環境で再現・切り分けできる情報を保存します。
最低限の保存対象:
秘密鍵、Wi-Fi認証情報、人間用パスワード、設置場所を特定する基準局の正確な座標は保存・公開しません。
実機・空・ネットワークを必要としません。
Wi-Fiを下位ネットワークとして使い、基準局宛はWireGuardを通します。
10.0.0.4 dev tun0 src 10.0.0.310.0.0.4:2101Runningこの段階ではFLOAT/FIXを必須条件にしません。得られた場合は記録します。
空の開けた場所で、トラクターを使わず実施します。
採取したデータはリポジトリへそのまま公開せず、位置情報と容量を考慮して保存場所を決めます。自動試験には匿名化・縮小したfixtureを使用します。
徒歩または安全な車載環境で実施します。
屋外へ持ち出すビルドは、次を満たす必要があります。
ゲート未達のビルドは現場へ持ち出しません。
P5-2で作る入出力境界と再生基盤を、P5-3以降の共通試験手段にします。
P5-2の実機試験で判明したバックグラウンド停止は、独立TrackでForeground Service化して解消し、完了後にP5-2のバックグラウンド複合障害試験を再実施します。
開発場所のタブレット実機で、Wi-Fi+WireGuardを使った本番経路を確認した。
24075RP89Gwataruap、タブレット 192.168.68.132Drogger-VRSC、タブレット 10.0.0.310.0.0.4:2101/RTK2B_BASEDG-PRO1RWS02タブレットから本番基準局へ直接NTRIPリクエストを送り、ICY 200 OKを確認した。実際の
RTKLIB casterはNTRIP v1のstatus lineをICY 200 OK+CRLFの1行だけ返し、HTTP形式の
header終端空行を送らない。この差により、空行まで待っていた初期実装は単体テストには合格する一方、
本番接続では失敗した。LiveNtripSourceFactoryを1行ICY応答に対応させ、Fake caster試験も本番と
同じ応答形式へ変更した。
修正版APKでは次を確認した。
Connectedへ遷移した。fixQualityが5(RTK FLOAT)から4(RTK FIX)へ移行した。rtcm.bin 44KB、nmea.bin 132KB、events.jsonl、metadata.jsonを保存した。続けてDG-PRO1RWS02の電源を約10秒切り、再投入するBluetooth復旧試験を行った。アプリは切断を
RECONNECTINGとして検知し、その間もNTRIP接続とRTCM3受信を継続した。再投入後は約15秒で
Bluetooth SPPへ自動再接続し、NMEA受信もchecksum不正0件で再開した。受信機電源断によって衛星捕捉も
初期化され、再接続後はfix_quality=0だった。開発場所は天空視界が狭いため、この試験では通信経路の
自動復旧を合格とし、FLOAT/FIX再取得時間の判定は見通しの良い屋外試験へ残す。
RTCM3フレーマーのdiscarded byteが増加するのは、基準局の同一NTRIPストリームにNMEAが混在する
既知の構成による。CRCが正常なRTCM3だけを抽出して受信機へ転送し、実際にFIXへ移行できたため、
段階Bの経路成立を妨げる異常とは判定しない。
これにより、Wi-Fi+WireGuardでの本番基準局接続、RTCM3抽出、Bluetooth書き戻し、補正後NMEA、
診断保存までの縦切り経路は成立した。次は切断・復旧試験、保存データのPC取り出し・再生、
Bluetoothテザリング、屋外標準データ採取を行う。
adbのrun-asでアプリ非公開領域(/data/data/dev.drogger.tablet/files/diagnostics/)から
セッションディレクトリをPCへ取り出せることを確認した。診断ログはgetExternalFilesDirではなく
アプリ非公開の内部ストレージへ保存されるため、取り出しにはrun-asまたはadb backup相当の手段が要る。
2026-08-01時点で2セッションを確認した。
20260801-115159: 修正前のNTRIP v1対応バグにより接続失敗が続いたセッション(rtcm.binは空)。20260801-115417: 修正後、NTRIP接続からBluetooth電源断・復旧試験までを含む約3.4時間の連続セッションrtcm.bin 6.6MB、nmea.bin 23MB)。events.jsonlはntrip/bluetooth/rtcm_writeの3カテゴリのみで、communication lost, retryingからconnectedまでの約15秒の復旧が記録されていた。基準局のRTCM 1005/1006メッセージは実座標を含むため、取り出した生ログはリポジトリへコミットしない。
2026-08-02追記: C1向けにreleaseビルド(debuggable無効)へ切り替えたところ、run-asが
run-as: package not debuggableで失敗し、上記の取り出し手段が使えなくなることを実機で確認した。
診断ログの保存先を内部ストレージ(filesDir)からアプリ固有の外部ストレージ
(getExternalFilesDir(null)、/sdcard/Android/data/dev.drogger.tablet/files/diagnostics/)へ変更し、
run-as無しでadb pullできることを確認した。C1以降はこの保存先・取り出し方法を使う。
adb pull /sdcard/Android/data/dev.drogger.tablet/files/diagnostics ./diagnostics
基準局座標を含む点は変わらないため、引き続きリポジトリへコミットしない。
取り出した実データをPC上のJVMユニットテストで、ライブ受信と同じ本番コード(RecordedRtcmSource→
Rtcm3Framer、SessionLogReader→parseSentence→FixRepository.ingestSentence)へ通して再生する
RealCapturedSessionReplayTestを追加した
(apps/android-tablet/app/src/test/java/dev/drogger/tablet/diagnostics/RealCapturedSessionReplayTest.kt)。
実データは基準局座標を含み得るためリポジトリへコミットせず、環境変数DROGGER_REAL_SESSION_DIRで
PC上のセッションディレクトリを指したときだけ実行し、未設定時はAssumeでskipする(testDebugUnitTestの
既定実行を妨げない)。
20260801-115417のデータで実行し、次を確認した。
fixQualityがRTK FLOAT(5)とRTK FIX(4)の両方を経由。testDebugUnitTest全体(262件、新規2件含む)も成功を確認した。
adb shell am force-stopでプロセスを強制終了し(正常終了経路を経ないため、直前セッションの
metadata.jsonへclosedAtMsは書かれない。異常終了を模した試験として妥当と判断した)、
adb shell am startで再起動した。
20260801-154558)が自動作成された。connected、Bluetooth connectedのイベントが記録された。rtcm.bin(16.9KB)・nmea.bin(157KB)が増加しており、RTCM書き戻しとadb shell svc wifi disable/enableでWi-Fiを切断・復旧した。
communication lost, retryingを検知し、以降10〜12秒間隔で3回のconnect failed(WireGuard tun0経由の接続試行がタイムアウト)を記録した。nmea.bin増加)は影響を受けなかった。10.0.0.4 dev tun0経路が復帰し、手動操作なしでconnectedへ戻った。ReconnectFailedやUI手動再接続には至らなかった。Wi-Fi切断はAndroidのWireGuardインターフェース自体を停止しないため、WireGuardトンネル自体の
ON/OFF試験(段階4)とは異なる障害系統として扱う。WireGuardアプリのトンネル切替はドキュメント上
UI手動操作のみで、adbから自動化する方法は確認できなかったため、この試験は利用者による手動操作と
併走して実施する。
WireGuardアプリへ切り替えてトンネルをOFF/ONした試験では、15:59:54から16:03:38まで製品アプリが
バックグラウンドとなり、heartbeatと診断ファイルの増加が停止した。トンネル経路自体は16:00:03から
16:01:01まで約56秒停止した後に復帰したが、製品アプリは前面へ戻るまでBluetooth/NTRIP切断を検知せず、
前面復帰直後の約1秒間にRTCM書込み失敗、Bluetooth/NTRIP通信断、両接続の復旧をまとめて記録した。
この結果はWireGuard断だけを独立に評価したものではないが、現行実装がバックグラウンドで測位・通信・
記録を継続できないことを示す実機事実である。製品要件は「バックグラウンドでも測位を記録し続ける」と
確定したため、前面表示必須という運用制約では受容しない。測位処理をActivity/ViewModelのライフサイクルから
分離してAndroid Foreground Serviceが所有し、常駐通知、通信断検知、自動再接続、プロセス再生成時の安全な
復旧を実装する。実装後は次を実機で再試験する。
RTKセッション(NTRIP・Bluetooth SPP・測位品質判定・診断ログ)の所有をActivity/ViewModelから
dev.drogger.tablet.service.RtkForegroundServiceへ移した。RtkSessionHost(plain Kotlin)が
RtkSession/FixRepository/DiagnosticRecorderを1回だけ構築し、start()/stop()を冪等にする。
BluetoothGpsViewModelはbindServiceでServiceの状態を中継するだけの薄いproxyへ変更し、
onCleared()ではunbindServiceのみ行いセッションは止めない。RtkSessionHostの冪等性・状態継続を
検証する自動試験を追加し、testDebugUnitTest(266件)・assembleDebugとも成功を確認した。
タブレット実機(24075RP89G、MIUI)へ導入し、次をすべてバックグラウンドのまま実施した(前面へ
戻したのは「前面復帰後の表示・二重起動なし」の確認時のみ)。
rtcm.bin/nmea.binも継続増加。instance=2f1e2e9)とも不変、dumpsys activity processesはisFrozen=false/procState=FGSをcommunication lost, retryingからconnectedまで約14秒でバックグラウンドのまま自動復旧。NTRIP・heartbeatは無影響のまま継続。手動試験3回中1回だけ発生した原因不明の長時間停止について、adb input tapでWireGuardアプリの
トンネルスイッチを直接操作する無人ループを作成し、原因切り分けを試みた。OFF待機10〜89秒・ON待機
5〜59秒をランダム化し、Droggerは前面へ戻さず15サイクル連続実行した(累積バックグラウンド時間は
最終的に約1248秒=20.8分に到達し、最初の手動試験でフリーズが発生した時点の累積バックグラウンド
時間(約17分)を上回った)。各サイクル後にheartbeatの鮮度を自動チェックし、閾値(20秒)を超える
遅延があればdumpsys activity processes・dumpsys deviceidle・フルlogcat・診断イベントを
自動保存する仕組みも用意した。
結果、15サイクルすべてでheartbeat_gapは2〜7秒に収まり、異常は1件も検出されなかった。累積
バックグラウンド時間や個々のOFF/ON待機時間を変えても再現しなかったことから、この停止は
特定のタイミングやMIUIの累積バックグラウンド時間しきい値に単純に依存するものではなく、
低頻度(手動+自動合わせて18試行中1回、約5.6%)で発生する非決定的な事象である可能性が高いと
判断する。原因は引き続き未特定のまま、既知の低頻度リスクとして記録する。
Track #37は、指示された自動試験・実機試験の項目をすべて実施した。WireGuardトンネル操作時に
3回中1回発生した原因不明の長時間停止だけが未解決のまま残るため、Trackはoutcome未確定・
in_progressのまま維持し、原因調査または再現頻度の監視を今後の課題とする。
PC2025(192.168.68.122)からSSH鍵の不一致が判明したため専用鍵ペアを新規生成し、
利用者が192.168.68.134側でauthorized_keysへ追記して解決した(詳細は
rtk-base-pi-zero-wh-setup.md、Issue #33コメント参照)。
利用者が基準局Piへ直接SSHし、sudo systemctl disable --now drogger-rtk-base.serviceで停止、
sudo systemctl enable --now drogger-rtk-base.serviceで再開した(タブレットは前面表示のまま)。
communication lost, retryingを検知し、以降6回ECONNREFUSEDreconnect failed after 6 attemptsでバックオフを使い切りReconnectFailedへ遷移した。ReconnectFailed中もBluetooth接続・NMEA受信は無影響のまま継続し、UIはgga_fix_quality_0)をinvalidFixCountとして正しく計上し続けた(誤った位置を表示し続けない)。ConnectivityManagerのネットワーク復帰コールバックが発火せず、ReconnectFailedからの自動復帰はしなかった。UIの「NTRIP再接続をリトライ」ボタンをConnected・fixQuality5(FLOAT)まで復帰した。基準局だけが止まり下位ネットワーク経路自体は継続している、という障害形態は、Wi-Fi/WireGuard断とは
異なりネットワーク復帰通知の対象外であるため、有限バックオフ終了後は利用者の手動再接続を要する。
これは「切断時に誤誘導せず、有限バックオフで再接続できる」というTrack #36の判断条件を満たす想定の
挙動であり、常時表示されるReconnectFailed状態と手動再接続ボタンが確実なフォールバックとして機能する
ことを実機で確認した。基準局断からの完全自動復帰は現時点のスコープに含めない。
Bluetoothテザリング経路の試験手順(準備)に沿って、開発場所(室内)でスマホAQUOS wish5とのBluetoothテザリングを検証した。DG-PRO1RWS02とはペアリング・SPP接続の実績が既にあり、スマホとは複数回ペアリング済みのため、今回はスマホ側のBluetoothテザリングON、タブレット側のペアリング済み機器で「インターネット接続」をONにする操作から開始した。
bt-panインターフェースがリンクアップ(UP,LOWER_UP)するのみでIPv4アドレスを取得せず、dumpsys connectivity上も実体のあるNetworkAgentInfoが生成されなかった(Wi-Fiが優先されている間はBluetooth PAN側のDHCPが完了しない挙動と推定される)。svc wifi disableでWi-Fiを無効化すると、bt-panが数秒内にDHCPでIPv4(10.44.67.243/24、DNS 10.44.67.220)を取得し、Bluetooth TetheringというVALIDATEDなNetworkAgentInfoが生成された。Drogger-VRSC)のUnderlyingNetworksがWi-FiからこのBluetooth Tetheringネットワークへ自動的に切り替わり、10.0.0.4宛の経路はtun0のまま維持された。ICMP ping自体は基準局側で応答がなく到達性確認には使えなかったが、実際のNTRIP(TCP 2101)は約0.4秒でcommunication lostからconnectedへ復帰し、以降fixQuality5(FLOAT)を維持しながらRTCM/NMEA受信・診断ログ保存が継続した。dumpsys bluetooth_manager上でも両方CLASSICAL-CONNECTEDのまま安定し、干渉や切断は観測されなかった。svc wifi enableでWi-Fiを復帰させ、WireGuardのUnderlyingNetworksがWi-Fiへ自動的に戻ることを確認した。この結果により、圃場を想定したBluetoothテザリング経由の下位ネットワーク切替がタブレット単体で成立することを室内で確認した。屋外実地でのテザリング品質・電池消費・スマホ側の挙動は屋外標準データ採取(C1)で確認する。
圃場ではスマホのBluetoothテザリングを下位ネットワークとする。開発場所でも室内でスマホを使って
事前検証できるため、屋外へ出る前に次の手順で成立を確認する。
ping 8.8.8.8が通る)。Drogger-VRSC)をON→OFF→ONし、tun0が10.0.0.3で有効になることを確認する。Connected、RTCM3受信、Bluetooth SPP接続、NMEA受信、診断ログ保存をこの手順は室内・開発場所で先に成立させ、失敗した場合は屋外へ持ち出す前にここで原因を切り分ける。
フィールドへ進むゲートを満たした上で、次の条件を実施前に確定する。
空の開けた場所で、NTRIPからRTCM3受信・Bluetooth送信・補正後NMEA受信・測位品質判定・診断ログ保存までの
縦切りを、開発環境の代わりに本番相当(release)ビルドで成立させる。段階Bまでは天空視界が狭い開発場所での
確認に留まっていた、DGNSS→FLOAT→FIXの遷移・静止座標分布・更新周期・遅延を、屋外の開けた環境で初めて
観測する。
C1の判断条件(通信断で誤誘導しないこと)には影響しないが、現地で遭遇し得る既知の制約・未実装機能を
事前に共有しておく。
./gradlew assembleReleaseで生成した署名済みreleaseビルド(apps/android-tablet/keystore/にlocal.propertiesに認証情報。ともにgit管理外)を使用する。releaseビルドは実機でのインストール・10.0.0.4:2101/RTK2B_BASEへ接続するapps/android-tablet/で./gradlew assembleReleaseを実行し、app/build/outputs/apk/release/app-release.apkadb installで導入する(署名鍵・パスワードはapps/android-tablet/keystore/とlocal.propertiesにWi-Fiが届かない屋外の開けた場所で、スマホBluetoothテザリング経由・releaseビルドでC1を実施した。
セッション20260802-092207(09:22:07〜09:32:25、約10.3分、STOPPED_BY_USERで正常終了)を実診断ログとして採取した。
communication lost, retryingが2回発生した後、以降は最後までconnectedを維持した。この結果により、Track #36の判断条件(基準局への接続・RTCM5種識別、Bluetooth同時送受信・測位品質判定、
記録再生の互換性、各種断からの誤誘導なし復旧、Wi-Fi+WireGuard室内試験合格、Bluetoothテザリング・
屋外標準データ採取)はすべて満たされたと判断できる状態になった。Track #36としての完了判断は別途行う。
生RTCM/生NMEAは基準局座標を含むため、取り出したログはリポジトリへコミットしない。