.dtrk)DTRK 記録フォーマットを、初見の人間・AI が離れた節を往復せずに理解できるよう再構成した正本。
この文書の位置づけ(正本)
この文書が 現行 DTRK 記録フォーマットの正本です。形式一覧、byte 構造、判定順、互換動作はこの文書を基準にします。Issue #132 は既存wire形式・実装の意味を変えず、情報設計を再構成したものです。
p6-trajectory-file-format.md は、V1〜V4の策定経緯・設計判断、schema 1の計画JSON詳細、失敗計画JSON、Stage 0判断改訂を残す 詳細・履歴資料です。この文書が旧文書の詳細節を明示的に参照している項目は、その参照先を補足定義として読みます。
両文書または実装との食い違いを見つけた場合は、仕様・実装いずれかの不具合として Issue #132 で差分を解消します。wire契約の記述が旧文書と食い違う場合は、この正本を優先します。
DTRK ファイルは、先頭 6byte の magic("DTRK")と version(UInt16)で大分類し、V4 だけはさらに 拡張 schema ID / version で細分類します。全フィールド big-endian。
| 形式 | version | header | record | 品質 | COG/速度 | event | payload | 用途・状態 |
|---|---|---|---|---|---|---|---|---|
| V1 | 1 | 36 | 10 | なし | なし | なし | なし | P6-9→P6-10 初版。実機記録あり。読み込みのみ対応。 |
| V2 | 2 | 40 | 13 | なし | あり | なし | なし | P6-11/12。アンテナ―作業機オフセットを header へ。読み込みのみ対応。 |
| V3 | 3 | 42 | 13 | なし | あり | なし | なし | Track #54。作業機幅を header へ追加。読み込みのみ対応。 |
| V4 plain | 4 | 46 | 13 | なし | あり | なし | なし | 旧通常形式/schema を持たない reader・writer 向けの最小 fallback。2026-09-06 取得データはこれ。 |
| V4 ID 1 v1 ( NAVIGATION_FIELD_TEST_DEBUG) |
4 | 50+JSON | 27 | あり | あり | あり | 計画 JSON | 現地ナビ検証 debug。planner 入力・結果・測位品質・ナビ状態・操作イベントを 1 ファイルから再現。 |
| V4 ID 2 v1 ( TRAJECTORY_POSITION_QUALITY)現行 |
4 | 50 | 27 | あり | あり | なし | なし | 現在 Tablet が新規作成する通常記録形式。各測位点へ 14byte の測位品質 suffix を付ける。計画 JSON も event も持たない。 |
Q. 現在 Tablet が新規作成する通常形式は?
A. V4 ID 2(TRAJECTORY_POSITION_QUALITY、headerLength=50 / recordLength=27)(Issue #131 以降)。records/ 直下に保存する。
46/13 の V4 plain は「schema を知らない reader/writer 向けの最小 fallback 形式」として仕様に残るだけで、Tablet が新規記録に使うことはない。
V4 の offset 46-49 のフィールドは、実装・旧文書では debugFormatId / debugFormatVersion と呼びます。しかし wire 上の役割は単に 「V4 base layout を拡張する schema の識別子」 です。
ID 2(TRAJECTORY_POSITION_QUALITY)は現地試験専用ではなく 通常の走行記録 が使うため、「debug」は歴史的な名称にすぎません。既存シンボルの一括改名はせず、この文書では次の対応で読みます。
| wire 上の役割 | 実装のコード名 |
|---|---|
| extension schema ID / version | debugFormatId / debugFormatVersion |
| extension header payload | debugHeaderPayload |
| record suffix | debugRecordSuffix |
本体 version(4)と extension schema version(ID 2 なら 1)は別物です。混同しないでください。
fileSize >= 6 を確認し、offset 0〜3 の magic(44 54 52 4B)を検証する。不一致は DTRK として扱わない。version(UInt16)を読み、V1〜V4 へ分岐する。未知 version は扱わない(空リストを返す)。36 / 10 固定。offset 6〜7(reserved、常に 0)を長さに使ってはならない。40 / 13 固定。V3: 42 / 13 固定。offset 6〜7 の headerLength は値としては書かれているが、既存 reader 実装との互換のため 境界計算には使わず version 別定数を使う。headerLength(UInt32, offset 6)と recordLength(UInt16, offset 44)を 実境界として使う。これが「ファイル内の長さを実利用する」契約の起点。fileSize < headerLength は不正とする。(fileSize - headerLength) % recordLength の余りは、電源断等による末尾の不完全 record/frame として切り捨てる。version == 4 と判定した後だけ base header 46byte を読む。V1〜V3 を 46byte header として読んではならない。headerLength > 46 なら offset 46-49 の debugFormatId / debugFormatVersion を読む。理解できない schema は offset 46 から headerLength まで skip する。headerLength から recordLength 刻みで frame を読む。elapsedMs の位置=frame marker)が 0 以上の frame だけを通常軌跡へ復元する。負値 frame・未知 suffix は skip する。(fileSize - headerLength) % recordLength の余りは末尾の不完全 frame として切り捨てる。負値 frame を event として解釈するのは debugFormatId/version == 1/1(NAVIGATION_FIELD_TEST_DEBUG v1)のときだけ。
未知 schema・未知 version・extension header 無しでは、負値 frame は意味を解釈せず単純に skip し、正常終了判定(cleanlyFinalized)もしない。ID 2 は負値 frame を生成も解釈もしない。
この節は各構成要素を それぞれ独立した完全表 で示す。値の表現は全て big-endian。scale 付きフィールドは指定倍率を掛けて四捨五入。「値なし」は各表のセンチネルを使い、上限超過はラップさせずセンチネルと衝突しない最大/最小有効値へ clamp する。
この 6byte は、将来 version がいくつ増えても offset・サイズ・意味を変更しない。ここだけを読めば「DTRK かどうか」「どの version の定義で残りを読むか」を判定できる。
| offset | size | 型 | 名前 | 内容 |
|---|---|---|---|---|
| 0 | 4 | ASCII | magic | "DTRK"(0x44 0x54 0x52 0x4B)。 |
| 4 | 2 | UInt16 | version | フォーマット本体 version(1〜4)。 |
offset 6 以降の意味は version によって変わる(V1 では reserved、V2 以降では headerLength)。V1〜V3 で共通に見える部分も、各 version が過去レイアウトを流用した結果であり恒久保証ではない。V4 で「以後の互換拡張で維持する base header / base record prefix」を改めて定義した。
segmentId は header に 1 回だけ。1e-7 度(赤道上で約 1.11cm)単位に丸める。flush()。レコード内の dLat / dLon は Int24。書き込みは 32bit int の下位 24bit を上位バイトから 3byte(big-endian)。読み込みは 3byte を読み、最上位 byte の最上位 bit が 1 なら上位 8bit を 0xFF で埋めて符号拡張。範囲 ±8,388,607 ≒ 開始点から約 ±93km。超過時はラップさせず境界値へ clamp。
version=1)| offset | size | 型 | 名前 | 内容 |
|---|---|---|---|---|
| 0 | 4 | ASCII | magic | "DTRK" |
| 4 | 2 | UInt16 | version | 1 |
| 6 | 2 | UInt16 | reserved | 0 固定(未使用)。長さとして解釈してはならない。 |
| 8 | 4 | Int32 | segmentId | このファイルが属するセグメント ID。 |
| 12 | 8 | Int64 | initialAtMs | セッション開始点の絶対 epoch ms。 |
| 20 | 8 | Float64 | initialLat | セッション開始点の絶対緯度(度、フル精度)。 |
| 28 | 8 | Float64 | initialLon | セッション開始点の絶対経度(度、フル精度)。 |
ファイルの最初の点(セッション開始点)は header が担うため、レコード列は 2 点目以降。
| offset | size | 型 | 名前 | 内容 |
|---|---|---|---|---|
| 0 | 4 | Int32 | elapsedMs | initialAtMs からの経過 ms(常に正)。 |
| 4 | 3 | Int24 | dLat | initialLat からの緯度オフセット(1e-7 度単位)。 |
| 7 | 3 | Int24 | dLon | initialLon からの経度オフセット(1e-7 度単位)。 |
version=2)| offset | size | 型 | 名前 | 内容 |
|---|---|---|---|---|
| 0 | 6 | — | magic / version | 共通識別部。version = 2。 |
| 6 | 2 | UInt16 | headerLength | 40 固定。V1 の reserved と同じ offset だが意味が異なる。境界計算には使わない(version 別定数)。 |
| 8 | 4 | Int32 | segmentId | V1 と同じ。 |
| 12 | 8 | Int64 | initialAtMs | V1 と同じ。 |
| 20 | 8 | Float64 | initialLat | V1 と同じ。 |
| 28 | 8 | Float64 | initialLon | V1 と同じ。 |
| 36 | 2 | Int16 | offsetForwardCm | 記録開始時点の Profile の前後オフセット(cm)。監査目的でセグメントごとに 1 回。 |
| 38 | 2 | Int16 | offsetLateralCm | 同、左右オフセット(cm)。 |
| offset | size | 型 | 名前 | 内容 |
|---|---|---|---|---|
| 0 | 4 | Int32 | elapsedMs | V1 と同じ。 |
| 4 | 3 | Int24 | dLat | V1 と同じ。 |
| 7 | 3 | Int24 | dLon | V1 と同じ。 |
| 10 | 2 | UInt16 | cogDeciDeg | この点の COG(進行方向)。0.1° 単位(0〜3599)。0xFFFF = 無効(RMC の COG フィールドが空欄)。RMC 由来をそのまま記録(位置差分から算出しない)。 |
| 12 | 1 | UInt8 | speedByte | この点の対地速度。0.05m/s 刻み(0〜254 = 0〜12.7m/s)。0xFF(255) = 無効。上限超過は無効ではなく 254 へ clamp。負値は無効。 |
version=3)| offset | size | 型 | 名前 | 内容 |
|---|---|---|---|---|
| 0 | 6 | — | magic / version | 共通識別部。version = 3。 |
| 6 | 2 | UInt16 | headerLength | 42 固定(境界計算には使わない)。 |
| 8 | 28 | — | segmentId / initialAtMs / initialLat / initialLon | V1 のレイアウトを流用(offset 8/12/20/28)。 |
| 36 | 2 | Int16 | offsetForwardCm | V2 から変更なし。 |
| 38 | 2 | Int16 | offsetLateralCm | V2 から変更なし。 |
| 40 | 2 | Int16 | implementWidthCm | 記録開始時点の Profile の作業機(ロータリー)全体幅(cm)。 |
V2 と 完全に同一(elapsedMs / dLat / dLon / cogDeciDeg / speedByte)。作業機幅は点ごとに変化しないため header にのみ持つ。
V4 は headerLength を UInt32 とし、offset 46 以降へ optional な extension header を収容できる。この 46byte layout は V4 策定時(Issue #96 / Track #121、旧 reader 未配布の段階)に初版として確定して以降、据え置き。V4 記録は 2026-09-06 に実機取得実績がある(§6.1)。
| offset | size | 型 | 名前 | 内容 |
|---|---|---|---|---|
| 0 | 4 | ASCII | magic | "DTRK" |
| 4 | 2 | UInt16 | version | 4 |
| 6 | 4 | UInt32 | headerLength | base header + optional extension header を含むヘッダ全体長。最小 46。実境界として使う。ホストの符号付き整数へ変換・確保する前に headerLength <= fileSize を検証する。 |
| 10 | 4 | Int32 | segmentId | V3 までと同じ意味。 |
| 14 | 8 | Int64 | initialAtMs | 全 frame 差分の基準 epoch ms。 |
| 22 | 8 | Float64 | initialLat | 全 frame 差分の基準緯度。 |
| 30 | 8 | Float64 | initialLon | 全 frame 差分の基準経度。 |
| 38 | 2 | Int16 | offsetForwardCm | 記録開始時 Profile snapshot。 |
| 40 | 2 | Int16 | offsetLateralCm | 記録開始時 Profile snapshot。 |
| 42 | 2 | Int16 | implementWidthCm | 記録開始時 Profile snapshot。 |
| 44 | 2 | UInt16 | recordLength | 1 frame 全体の byte 数。最小 13。実境界として使う。 |
headerLength > 46 のときだけ、offset 46 から)| offset | size | 型 | 名前(コード名) | 内容 |
|---|---|---|---|---|
| 46 | 2 | UInt16 | extension schema ID(debugFormatId) | 拡張 schema の種別。0 は予約。 |
| 48 | 2 | UInt16 | extension schema version(debugFormatVersion) | この schema の version。 |
| 50 | 可変 | byte 列 | extension header payload(debugHeaderPayload) | 長さ = headerLength - 50(別フィールドを持たない)。schema 固有 metadata。 |
well-formed な V4 の条件
46 <= headerLength <= fileSize、13 <= recordLength <= 65535。headerLength が 47〜49 は不正(extension header の途中)。headerLength > 46 なら headerLength >= 50。recordLength > 13(suffix 付き frame)なら headerLength >= 50(suffix の schema を識別できる extension header が必須)。不正値からの部分復旧範囲は reader 実装の裁量(実装は header の初期値から 記録開始点 1 点だけ回収する degraded 動作)。
offset headerLength から recordLength 刻みで固定長 frame が並ぶ。frame 先頭の Int32 が 0 以上なら通常測位 frame、負値なら非測位 frame。通常測位 frame の先頭 13byte は V2/V3 レコードと同一の値表現。
| offset (frame 内) | size | 型 | 名前 | 内容 |
|---|---|---|---|---|
| 0 | 4 | Int32 | elapsedMs(frame marker) | initialAtMs からの経過 ms。通常測位は 0 以上。負値は非測位 frame(意味は schema 定義)。 |
| 4 | 3 | Int24 | dLat | initialLat からの差分(1e-7 度単位)。 |
| 7 | 3 | Int24 | dLon | initialLon からの差分(1e-7 度単位)。 |
| 10 | 2 | UInt16 | cogDeciDeg | 0.1 度単位、0xFFFF = 無効。 |
| 12 | 1 | UInt8 | speedByte | 0.05m/s 単位、0xFF = 無効。 |
V4 では anchor を「header だけに存在する暗黙の 1 点」とせず、最初の測位点も 1 件目の frame として書く(通常は elapsedMs=0, dLat=0, dLon=0)。V4 の点数は通常測位 frame 数であり event frame を含めない。V1〜V3 reader は従来どおり header anchor を 1 点目として復元する。
これは 特定 schema の配下ではなく中立な共通節。(ID 1, version 1) と (ID 2, version 1) の両方が、通常測位 prefix 13byte の後ろへ この同一 14byte を置く(recordLength = 13 + 14 = 27)。両者を同じ codec で読む。未知 version の suffix は品質として解釈せず skip する(既知部分=通常測位 prefix だけ回収)。
各通常測位 frame の元になった GGA fix と、その時点で紐付いた RMC / GST の品質情報を保存する。offset は suffix 内(= frame 内 offset 13 が suffix offset 0)。
| offset (suffix 内) | size | 型 | 名前 | 内容 | センチネル(値なし) |
|---|---|---|---|---|---|
| 0 | 1 | UInt8 | ggaFixQuality | 生 GGA fix quality。 | 0 = suffix 全体が NOT_RECORDED |
| 1 | 1 | UInt8 | numSatellites | 使用衛星数(0〜254)。 | 0xFF |
| 2 | 2 | UInt16 | hdopCenti | HDOP × 100 を四捨五入(0〜65534)。 | 0xFFFF |
| 4 | 2 | UInt16 | gstHorizontalErrorMm | GST 水平誤差 m × 1000 を四捨五入。 | 0xFFFF |
| 6 | 4 | Int32 | altitudeCm | GGA 標高 m × 100 を四捨五入(−2147483647〜2147483647)。 | 0x80000000 |
| 10 | 2 | UInt16 | rmcAgeMs | base record 時刻から直近 RMC までの経過 ms(max(0, 差))。 | 0xFFFF |
| 12 | 2 | UInt16 | gstAgeMs | base record 時刻から直近 GST までの経過 ms(max(0, 差))。 | 0xFFFF |
ggaFixQuality = 0 なら、残り 13byte の値にかかわらず suffix 全体を NOT_RECORDED とする。これは、拡張を知らない writer が 14byte を全 0 で追記した場合と区別できない(=そのまま「未取得」扱い)。有効な suffix が偶然全 byte 0 になり得る schema はそのまま登録してはならない。
NOT_RECORDED の点は runtime model(TrackPoint.quality)としては null(品質記録なし)になり、品質区分は UNKNOWN になる。
NAVIGATION_FIELD_TEST_DEBUG、debugFormatId=1 / version 1)| 領域 | offset | size | 内容 |
|---|---|---|---|
| base header | 0 | 46 | 3.5 のとおり。recordLength = 27 固定。 |
| extension schema ID | 46 | 2 | 1 |
| extension schema version | 48 | 2 | 1 |
| extension header payload | 50 | headerLength − 50 | BOM なし・無圧縮 UTF-8 JSON の object。1〜1,048,576 byte。従って headerLength = 50 + JSON長。 |
JSON payload の必須 top-level field は sessionId / capturedAtMs / app / planner / request / result。header では result.status = SOLVED かつ finalTillagePlanResult = Solved を必須とする。Stopped を含む結果は .plan.json(失敗・非実走計画)でのみ使う。詳細なフィールド定義は、本正本が参照する補足定義として p6-trajectory-file-format.md「debugFormatId 1」を参照する。
| offset(frame 内) | size | 内容 |
|---|---|---|
| 0 | 13 | 通常測位 prefix(3.6)。ただし marker が負値なら event frame(3.10)。 |
| 13 | 14 | 共通 position quality suffix(3.7)。 |
ID 1 は負値 marker frame を event frame(-1=EVENT_START / -2=EVENT_CONTINUATION)として使い、GNSS 点がない間も同じ 27byte 固定長列へ event を追記する。
TRAJECTORY_POSITION_QUALITY、debugFormatId=2 / version 1) 現行通常記録各測位点の測位品質を、計画 JSON・event frame を持たずに保存する拡張 schema(Issue #131)。通常の新規 DTRK 記録はこの schema で記録する。低品質点も解析・監査に必要な一次データとして全点保存し、色分け・除外は表示時の非破壊フィルタで行う。
headerLength = 50 / recordLength = 27)| offset | size | 型 | 名前 | 値 |
|---|---|---|---|---|
| 0 | 4 | ASCII | magic | "DTRK" |
| 4 | 2 | UInt16 | version | 4 |
| 6 | 4 | UInt32 | headerLength | 50(base 46 + ID 2 + version 2)。writer は常にこの値。仕様上 ID 2 は headerLength = 50 固定で、それ以外は非正規(下の reader 実挙動を参照)。 |
| 10 | 4 | Int32 | segmentId | セグメント ID |
| 14 | 8 | Int64 | initialAtMs | 基準 epoch ms |
| 22 | 8 | Float64 | initialLat | 基準緯度 |
| 30 | 8 | Float64 | initialLon | 基準経度 |
| 38 | 2 | Int16 | offsetForwardCm | 記録開始時 Profile snapshot |
| 40 | 2 | Int16 | offsetLateralCm | 記録開始時 Profile snapshot |
| 42 | 2 | Int16 | implementWidthCm | 記録開始時 Profile snapshot |
| 44 | 2 | UInt16 | recordLength | 27 固定 |
| 46 | 2 | UInt16 | extension schema ID | 2 |
| 48 | 2 | UInt16 | extension schema version | 1 |
extension header payload は 0byte。reader は headerLength == 50 のとき debugHeaderPayload を空配列ではなく null へ正規化して返す(debugHeaderPayload != null を計画 JSON 存在の判定に使う既存 consumer は、先に debugFormatId == 1 で gate すること)。
13 + 14 = 27 の完全な合成表ID 2 の 1 レコードは、参照ではなく次の 1 表だけで理解できる。frame 内 offset で通しで示す。
| frame 内 offset | size | 型 | 名前 | 内容 / センチネル |
|---|---|---|---|---|
| 0 | 4 | Int32 | elapsedMs | initialAtMs からの経過 ms。ID 2 は常に 0 以上(負値 marker を生成しない)。 |
| 4 | 3 | Int24 | dLat | initialLat からの差分(1e-7 度単位)。 |
| 7 | 3 | Int24 | dLon | initialLon からの差分(1e-7 度単位)。 |
| 10 | 2 | UInt16 | cogDeciDeg | COG、0.1 度単位(0〜3599)。0xFFFF = 無効。 |
| 12 | 1 | UInt8 | speedByte | 対地速度、0.05m/s 単位(0〜254)。0xFF = 無効。 |
| 13 | 1 | UInt8 | ggaFixQuality | 生 GGA fix quality。0 = この点全体が NOT_RECORDED(品質記録なし)。 |
| 14 | 1 | UInt8 | numSatellites | 使用衛星数(0〜254)。0xFF = 個別値なし。 |
| 15 | 2 | UInt16 | hdopCenti | HDOP × 100 を四捨五入。0xFFFF = 個別値なし。 |
| 17 | 2 | UInt16 | gstHorizontalErrorMm | GST 水平誤差 m × 1000 を四捨五入。0xFFFF = 個別値なし。 |
| 19 | 4 | Int32 | altitudeCm | GGA 標高 m × 100 を四捨五入。0x80000000 = 個別値なし。 |
| 23 | 2 | UInt16 | rmcAgeMs | base record 時刻から直近 RMC までの経過 ms。0xFFFF = 個別値なし。 |
| 25 | 2 | UInt16 | gstAgeMs | base record 時刻から直近 GST までの経過 ms。0xFFFF = 個別値なし。 |
現行の DtrkFileReader(DtrkV4.isWellFormed())は ID 2 固有の固定長検査を持たない。判定するのは §3.5 の共通 well-formed 条件(46 <= headerLength <= fileSize、13 <= recordLength <= 65535、headerLength 47〜49 不正、headerLength > 46 なら >= 50、recordLength > 13 なら headerLength >= 50)だけ。従って:
headerLength < 46/headerLength > fileSize/headerLength が 47〜49/recordLength < 13/recordLength > 65535/recordLength > 13 かつ headerLength < 50。headerLength > 50、recordLength が 27 以外だが 13〜65535)でも degraded にはならず、reader は通常測位 prefix と、recordLength - 13 == 14 のときは 14byte 品質 suffix を通常どおり回収する。headerLength > 50 のときだけ debugHeaderPayload が非 null になる点が正規形と異なる。headerLength != 50 を弾く)するかは 未決。reader 厳格化は互換性に影響する仕様変更のため、別途利用者承認が必要。recordEvent / pending event / SESSION_FINALIZED は ID 2 では常に no-op。よって ID 2 ファイルに cleanlyFinalized の概念はない(V4 plain 46/13 と同じく中断/正常終了を区別しない)。records/ 直下に保存する(records/debug/ は ID 1 専用)。46/13」「通常品質 ID 2」「NAV debug ID 1」の 3 ブランチを明示し、debugHeaderPayload の有無を保存先・factory 選択の代理条件にしない。supportsEvents は suffix 長ではなく debugFormatId == 1 で判定する(ID 2 も 14byte suffix を持つため、suffix 長で判定すると通常記録が意図せず event 対応になる)。ID 1 は負値 frame を event frame として使う。1 event の全 frame は他 frame を挟まず連続して書き、完了後に flush する。frame サイズは recordLength(27)。
marker = -1)| offset | size | 型 | 名前 | 内容 |
|---|---|---|---|---|
| 0 | 4 | Int32 | marker | -1 |
| 4 | 8 | Int64 | eventAtMs | event 発生時の絶対 epoch ms。 |
| 12 | 4 | UInt32 | eventSequence | ファイル内で 1 から単調増加。 |
| 16 | 2 | UInt16 | eventType | 下表(1〜10)。未知 eventType は skip。 |
| 18 | 2 | UInt16 | payloadLength | UTF-8 JSON byte 数。最大 65535。 |
| 20 | 7 | byte 列 | payload 先頭 | 不足分は 0 padding(27 − 20 = 7)。 |
marker = -2)| offset | size | 型 | 名前 | 内容 |
|---|---|---|---|---|
| 0 | 4 | Int32 | marker | -2 |
| 4 | 4 | UInt32 | eventSequence | START と同じ値。 |
| 8 | 2 | UInt16 | chunkIndex | 1 から連番。 |
| 10 | 17 | byte 列 | payload 続き | 最終 chunk 不足分は 0 padding(27 − 10 = 17)。 |
reader は payloadLength 分だけを連結する。sequence / chunkIndex 不一致、途中欠落、不正 JSON の event 全体を無視し、通常軌跡は維持する。
| eventType | 名前 | payload 必須 field(抜粋) |
|---|---|---|
| 1 | RECORDING_STARTED | eventName、reason(USER / PROCESS_RESTART / FORMAT_CHANGED / PLAN_CONFIRMED / CLEAR_CONTINUATION) |
| 2 | RECORDING_PAUSED | eventName、reason(USER / SYSTEM) |
| 3 | RECORDING_RESUMED | eventName |
| 4 | PLAN_CONFIRMED | eventName、planRevision |
| 5 | NAVIGATION_STATE_CHANGED | eventName、from、to、reasonCode(前方定義、未実装の間は生成しない) |
| 6 | TARGET_ACTION_CHANGED | eventName、fromActionIndex / toActionIndex、trigger(同上) |
| 7 | USER_OPERATION | eventName、operationId、value |
| 8 | WARNING | eventName、code、stage / detail |
| 9 | ERROR | eventName、code、stage / exceptionClass / message |
| 10 | SESSION_FINALIZED | eventName、outcome、endedAtMs、pointRecordCount、eventCount、droppedEventCount、writeErrorCount、lastActionIndex、coverage |
SESSION_FINALIZED は正常に file を閉じる際の最後の event。final event が無ければ中断終了、final event より後に frame があれば不正 file。
[1] magic == "DTRK" ? ─ no ─▶ DTRK ではない(空リスト)
│ yes
[2] version は?
├─ 1 ─▶ header 36 / record 10 固定。COG/速度/品質なし。
├─ 2 ─▶ header 40 / record 13 固定。COG/速度あり、品質なし。
├─ 3 ─▶ header 42 / record 13 固定。+ implementWidthCm。品質なし。
├─ 4 ─▶ [3] へ
└─ その他 ─▶ 未知 version(空リスト)
[3] V4: headerLength(UInt32@6) / recordLength(UInt16@44) を読む
│ well-formed でない ─▶ degraded(header の初期値から記録開始点 1 点だけ回収)
│ headerLength == 46 ─▶ V4 plain。品質なし。event なし。
│ headerLength > 46 ─▶ [4] へ
[4] extension schema ID(UInt16@46) / version(UInt16@48) は?
├─ (1, 1) NAVIGATION_FIELD_TEST_DEBUG ─▶ 27byte frame。13byte prefix + 14byte 品質 suffix。
│ 負値 frame を event として解釈(EVENT_START/CONTINUATION、SESSION_FINALIZED 検出)。
│ header payload = 計画 JSON。
├─ (2, 1) TRAJECTORY_POSITION_QUALITY ─▶ 27byte frame。13byte prefix + 14byte 品質 suffix。
│ 負値 frame なし。正規形は headerLength==50 / recordLength==27(reader は
│ headerLength>=50 なら通し、ID 2 固有の固定長検査はしない)。★現行通常記録
└─ 未知 ID / 未知 version ─▶ offset 46〜headerLength を skip。
recordLength 刻みで通常測位 prefix 13byte だけ回収。
suffix は品質として解釈しない。負値 frame は解釈せず skip。
[*] 末尾: (fileSize - headerLength) % recordLength の余りは切り捨て。
| 状況 | 作られる形式 | writer factory |
|---|---|---|
| 通常の新規記録(現行) | V4 ID 2(50/27) | DtrkV4Writer.startTrajectoryPositionQuality() |
| 現地ナビ検証 debug(計画確定後) | V4 ID 1(50+JSON / 27) | DtrkV4Writer.startNavFieldTestDebug() |
| schema 拡張を使わない最小 fallback | V4 plain(46/13) | DtrkV4Writer.startNew() |
| (旧)P6-10〜Track #54 の新規記録 | V3(42/13) | DtrkFileWriter.startNew()(現在は新規記録に使わない) |
V4 はプロセス再起動をまたぐ既存 file への resume-append を行わない。再開時は新しい segmentId と新しい file を開始する。稼働中の「記録を停止」→「記録を再開」だけ、同じプロセスが保持する同一 file へ追記する。
| reader | 入力形式 | 位置/時刻 | COG/速度 | 品質 | event |
|---|---|---|---|---|---|
| V1〜V3 reader | V1/V2/V3 | ○ | V2/V3 のみ ○ | — | — |
| V1〜V3 reader | V4(いずれも) | ✕ 扱えない(46byte header として読まない契約) | |||
| V4 reader(ID 2 非対応) | V4 ID 2 50/27 | ○ | ○ | ✕(未知 schema として suffix skip) | — |
| V4 reader(現行、ID 1/2 対応) | V4 plain 46/13 | ○ | ○ | ✕(suffix なし → 全点 UNKNOWN) | — |
| V4 reader(現行) | V4 ID 2 50/27 | ○ | ○ | ○(14byte suffix) | —(ID 2 は event なし) |
| V4 reader(現行) | V4 ID 1 50+JSON/27 | ○ | ○ | ○(14byte suffix) | ○(負値 frame) |
| V4 reader(現行) | V4 未知 schema / 未知 version | ○ | ○ | ✕(既知部分のみ回収) | ✕(負値 frame は skip、cleanlyFinalized なし) |
| V4 reader | V4 で headerLength/recordLength 不正・payload 上限超過 | △ 記録開始点 1 点のみ(degraded) | — | — | — |
46/13 を UNKNOWN として扱う根拠V1〜V3 と V4 plain(46/13)は測位品質 suffix を持たない。従って TrackPoint.quality は null になり、品質区分は UNKNOWN(品質記録なし)になる。
ggaFixQuality = 0(NOT_RECORDED)の点は同様に UNKNOWN。| 状況 | 扱い |
|---|---|
| 末尾の不完全 record/frame(電源断等) | (fileSize - headerLength) % recordLength の余りとして自動的に切り捨てる。それまでは有効。 |
| 途中レコードのビット化け | その点の位置がおかしくなるだけ。前後のレコードには影響しない(header の初期値のみに依存)。 |
| header 自体の破損(サイズ不足・magic 不一致) | ファイル全体を扱えないものとして空リストを返す。部分復旧はしない。 |
未知 eventType | その event frame を skip。通常軌跡は維持。 |
| event の sequence/chunkIndex 不一致・途中欠落・不正 JSON | その event 全体を無視し droppedEventCount を増やす。通常軌跡は維持。 |
| ID 1 の header payload が不正 UTF-8・parse 不能・必須 field 欠落 | 通常軌跡は復元し、計画再構成だけを不可とする。 |
| ID 1 の header payload が 1MiB 超過 | reader は一括確保前に弾き、header の記録開始点 1 点だけ回収(degraded)。writer は debug 記録開始を失敗させ、通常 V4 へフォールバック。 |
| 未知 schema での負値 frame | 意味を解釈せず単純に skip。SESSION_FINALIZED も検出しない。 |
| wire 上の役割 | 実装のコード名 / 定数 | 補足 |
|---|---|---|
| 本体フォーマット version | version(DtrkFormat.VERSION_1..3 / DtrkV4.VERSION) | 1〜4。 |
| extension schema ID | debugFormatId(DtrkV4Debug.*_ID) | 「debug」は歴史的名称。ID 2 は通常記録用。 |
| extension schema version | debugFormatVersion(DtrkV4Debug.*_VERSION) | 本体 version とは独立。 |
| extension header payload | debugHeaderPayload | ID 1 は計画 JSON、ID 2 は 0byte(null 正規化)。 |
| record suffix | debugRecordSuffix | ID 1/2 v1 は共通の 14byte 品質 suffix。 |
| frame marker | frame 先頭 Int32(elapsedMs の位置) | >= 0 通常測位 / < 0 非測位。 |
| セッション開始点 | DtrkAnchor / initial* | 全レコード差分の基準。 |
ggaFixQuality(生 GGA fix quality の数値)、gstHorizontalErrorMm、hdopCenti、各 age など。DTRK には全項目を保存する。TrajectoryPointQuality(初弾は ggaFixQuality / gstHorizontalErrorM / gstAgeMs の最小限)と、そこから求める TrajectoryQualityClass。この分類の 正本は p6-ui-spec.md「走行軌跡の測位品質記録・色分け・除外」。実装は track-core の classifyTrajectoryQuality() に一元化し、Tablet と PC で別実装にしない。現時点の対応(参照用の要約、正本は上記):
| GGA 値 | 区分 | 表示文言 | 色 |
|---|---|---|---|
| 4 | FIX | 固定解 | 緑 |
| 5 | FLOAT | 浮動解 | 黄 |
| 2 | DGNSS | 差分測位 | 橙 |
| 1 | SINGLE | 単独測位 | 赤 |
| その他の正値 | OTHER | その他の測位 | 青/紫 |
sample なし / 0 | UNKNOWN | 品質記録なし | 灰 |
0 の無効 fix は TrackPoint を生成しない。記録済み点を INVALID に分類しない。無効期間は点の欠落として現れ、前後を接続しない。STALE(受信後経過時間に依存するライブ状態)は保存点の恒久分類に入れない。表示は「① TrajectoryRenderGrouping で記録 segment ごとに分ける ② 中心線 centerline() / 帯 apply(offsets) を適用(旋回除外・renderGroupId 採番) ③ 変換後の点列を品質区分・フィルタ非表示 gap で品質 run へ分ける ④ 最終描画キーを (qualityRunId, renderGroupId) 相当とする」の順で固定する。品質 run ID を永続 segmentId や renderGroupId へ上書きしない。詳細は p6-ui-spec.md と pc-trajectory-viewer.md。
46/13(2026-09-06 取得データ)samples/fieldData/デフォルト_20260906-085610.dtrk(209,827 byte = 46 + 13 × 16137)。この形式は V4 plain であり、V2(40/13)ではない(先頭 6byte で version=4、offset 6 の UInt32 が 46)。
offset hex フィールド デコード
00 44 54 52 4B magic "DTRK"
04 00 04 version 4
06 00 00 00 2E headerLength 46(UInt32)
0A 00 00 00 01 segmentId 1
0E 00 00 01 A0 74 00 47 67 initialAtMs 1788652570471(2026-09-06 08:56:10.471 JST)
16 40 40 9A 3E FC 05 E7 11 initialLat 33.20504713333333
1E 40 60 A3 66 1A F8 0C 4F initialLon 133.10621403166667
26 00 32 offsetForwardCm 50
28 00 00 offsetLateralCm 0
2A 00 96 implementWidthCm 150
2C 00 0D recordLength 13(UInt16)
offset hex フィールド デコード
2E 00 00 00 00 elapsedMs 0(= 最初の測位点。V4 は anchor も frame として書く)
32 00 00 00 dLat 0
35 00 00 00 dLon 0
38 01 C8 cogDeciDeg 456 → 45.6°
3A 00 speedByte 0 → 0.0 m/s
2E+13 00 00 00 4E elapsedMs 78 ms
00 00 00 / 00 00 00 dLat/dLon 0 / 0(開始点とほぼ同一位置)
01 C8 / 00 cog/speed 45.6° / 0.0 m/s
品質 suffix なし・event なし。この 16,137 点はすべて品質区分 UNKNOWN(4.4)。
50/27(現行通常記録、Issue #131 以降)Issue #131 以降に Tablet が新規作成する通常記録はこの形式。実機データはまだ無いため合成例で示す。
offset hex フィールド 値
00 44 54 52 4B magic "DTRK"
04 00 04 version 4
06 00 00 00 32 headerLength 50(UInt32、!= 50 は不正)
0A <4byte> segmentId
0E <8byte> initialAtMs
16 <8byte> initialLat(Float64)
1E <8byte> initialLon(Float64)
26 00 0A offsetForwardCm 10(例)
28 00 00 offsetLateralCm 0
2A 00 96 implementWidthCm 150(例)
2C 00 1B recordLength 27(UInt16)
2E 00 02 extension schema ID 2
30 00 01 extension schema version 1
DtrkPositionSample(ggaFixQuality=4, numSatellites=20, hdop=0.7, gstHorizontalErrorM=0.012, altitudeM=100.0, rmcAgeMs=30, gstAgeMs=100):
frame内 hex フィールド デコード
00 00 00 00 00 elapsedMs 0
04 00 00 00 dLat 0
07 00 00 00 dLon 0
0A 01 C8 cogDeciDeg 45.6°
0C 00 speedByte 0.0 m/s
─── ここから 14byte position quality suffix ───
0D 04 ggaFixQuality 4 → FIX(固定解)
0E 14 numSatellites 20
0F 00 46 hdopCenti 70 → HDOP 0.70
11 00 0C gstHorizontalErrorMm 12 → 0.012 m
13 00 00 27 10 altitudeCm 10000 → 100.00 m
17 00 1E rmcAgeMs 30 ms
19 00 64 gstAgeMs 100 ms
0D..1A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 suffix 全 0 → NOT_RECORDED → 品質区分 UNKNOWN
(拡張を知らない writer が 14byte を全 0 で埋めた場合と区別できない=そのまま「未取得」扱い。3.7)
V4 plain 46/13 | V4 ID 1 50+JSON/27 | V4 ID 2 50/27 | |
|---|---|---|---|
| 各点の測位品質 | なし(全点 UNKNOWN) | あり(14byte suffix) | あり(14byte suffix) |
| event frame | なし | あり(負値 marker) | なし |
| header payload | なし | 計画 JSON(1〜1MiB) | なし(0byte) |
cleanlyFinalized の概念 | なし | あり(SESSION_FINALIZED) | なし |
| 保存先 | records/ | records/debug/ | records/ |
| 用途 | schema 非対応向け最小 fallback | 現地ナビ検証 debug | 現行の通常走行記録 |
apps/android-tablet/track-core)| ファイル | 役割 |
|---|---|
track/DtrkFormat.kt | V1〜V4 共通の定数・prefix writer・COG/速度 codec。HEADER_SIZE_V1..3、RECORD_SIZE_V1..3、COG_INVALID、SPEED_INVALID。 |
track/DtrkV4.kt | V4 の offset 定数と isWellFormed()。BASE_HEADER_SIZE=46、MIN_DEBUG_HEADER_SIZE=50、BASE_RECORD_PREFIX_SIZE=13、OFF_*。 |
track/DtrkV4Writer.kt | V4 writer。startNew() / startTrajectoryPositionQuality() / startNavFieldTestDebug() の 3 ブランチ。DtrkV4Debug(ID/version 定数)。 |
track/DtrkFileWriter.kt | V1〜V3 writer(新規は V3、resume() は既存形式を維持)。 |
track/DtrkFileReader.kt | V1〜V4 reader。readAll() / readV4Detail() / decodeV4Detail() / degraded 経路。 |
track/DtrkPositionSample.kt | 14byte 品質 suffix の DTO と DtrkPositionSampleCodec(encode/decode・センチネル・clamp)。 |
track/DtrkV4EventFrames.kt | event frame(負値 marker)の frame レベル codec。MARKER_EVENT_START=-1、MARKER_EVENT_CONTINUATION=-2、SESSION_FINALIZED_TYPE_ID=10。 |
model/TrajectoryQualityClass.kt | classifyTrajectoryQuality()(GGA 値 → 区分)と TrackPoint.qualityClass。 |
model/TrajectoryPointQuality.kt | runtime の品質 model(最小限)。 |
track/DtrkV4TrajectoryPositionQualityTest.kt — ID 2 writer が 50byte header / 27byte frame / debugFormatId=2 / version 1 を書くこと、14byte suffix の round-trip(FIX→DGNSS→FLOAT→FIX→UNKNOWN)。track/DtrkV4Test.kt / DtrkV4DebugTest.kt / DtrkV4EventTest.kt — V4 base layout、schema 1 suffix、event frame の分割・再結合・cleanlyFinalized。track/DtrkFormatTest.kt — COG(0xFFFF)・速度(0xFF / 254 clamp)のセンチネルと量子化。track/DtrkFileWriterReaderTest.kt — V1(36byte header)・V2(40byte header)・V3(42byte header)の読み書きと resume の形式維持。samples/fieldData/*.dtrk(2026-08-05 は V2、2026-09-06 は V4 plain 46/13)。.dtrk ビューア/耕耘あと表示検証ツール。