12.3. パケット形式

カメラとホストの間でワイヤを越えるすべてのバイトは、パケットの一部です。パケットは10バイトのヘッダーで始まり、可変長のペイロードが続き、4バイトの末尾CRCで終わります。それ以外のバイトはワイヤ上に現れません。ホストが2バイトの同期ワードを見つけたら、続くバイトはこの正確な順序のヘッダーです。

プロトコルパケットの水平レイアウト。10バイトのヘッダー(同期ワード、シーケンス番号、チャネルID、フラグ、オペコード、ペイロード長、ヘッダーCRC)に続いて、可変長のペイロードと4バイトのペイロードCRCが示されている。

12.3.1. ヘッダー

パディングなしで詰め込まれた10バイトです。各フィールドは次のとおりです。

  • sync -- リトルエンディアン順の16ビットワード 0xD5AA。ワイヤ上のバイト0は 0xAA、バイト1は 0xD5 です。バイトをスキャンする受信側は、AA D5 のペアを探すことでパケットの先頭を見つけられます。その前にあるものはすべてゴミとして扱われます。この値の選択は意図的です。0xAA0xD5 は印刷可能なテキストにめったに現れず、このペアがペイロードの途中に偶然現れる可能性は低いからです。

  • seq -- 1バイト。所定の方向に送信される各パケットごとに1ずつ増加するカウンタです。受信側は次のパケットのシーケンス番号が期待どおりかを確認します。そうでなければ、信頼性レイヤーが再送を要求します。

  • chan -- 1バイト。このパケットが属するチャネルIDです。チャネル0~31が使用可能です。組み込みの stdinstdoutstream、そして(オプションで)profile チャネルは、カメラが予約する固定IDを使用します。

  • flags -- 1バイト。受信側にパケットの解釈方法を伝えるビットフィールドです。

    • ビット0 ACK -- このパケットは直前のパケットに対する確認応答です。

    • ビット1 NAK -- このパケットは直前のパケットを拒否します。

    • ビット2 RTX -- このパケットは再送です。

    • ビット3 ACK_REQ -- 送信側はこのパケットの確認応答を求めています。

    • ビット4 FRAGMENT -- より大きなメッセージの中で、このパケットの後にさらにフラグメントが続きます。

    • ビット5 EVENT -- このパケットはデータではなくチャネルイベントを運びます。

    • ビット6と7は予約済みです。

  • opcode -- 1バイト。コマンドまたは応答のコードです。プロトコルライブラリは、用途別にオペコードの範囲を予約しています。

    • 0x00..0x0F -- プロトコルコマンド(SYNC、GET_CAPS、SET_CAPS、STATS、VERSION)。

    • 0x10..0x1F -- システムコマンド(RESET、BOOT、INFO、EVENT、MEMORY)。

    • 0x20..0x2F -- チャネルコマンド(LIST、POLL、LOCK、UNLOCK、SHAPE、SIZE、READ、WRITE、IOCTL、EVENT)。

  • len -- 2バイト、リトルエンディアン。ヘッダーに続くペイロードバイトの数です。長さゼロは正当です。多くの確認応答や小さなコマンドはペイロードを持ちません。

  • crc -- 2バイト。直前の8バイトのヘッダーに対するCRC-16です。不正なCRCを持つヘッダーを受け取った受信側は、ペイロードを見ることすらせずにパケット全体を破棄します。

12.3.2. ペイロード

0バイト以上で、フレーミングレイヤーからは不透明なものとして扱われます。ペイロードの中身はオペコードに依存します。CHANNEL_READ の応答では実際のチャネルデータであり、GET_CAPS の応答では小さな固定構造であり、チャネル書き込みではホストが送ったものそのままです。

最大ペイロードサイズはカメラのプロトコルバッファサイズに依存します(protocol.init() のボードごとの表を参照してください)。上限より長いメッセージは、最後を除くすべてに FRAGMENT フラグを立てたフラグメントに分割されます。

12.3.3. 末尾CRC

4バイト、ペイロードに対するCRC-32です。ヘッダーCRCでは検出できない破損、特にフレーム途中の1ビットエラーがすり抜けてしまうような長いペイロードでの破損を捕捉します。

整合性チェックを2つのCRCに分割しているのは意図的です。ヘッダーCRCはフレーミングフィールド自体、特にペイロード長を保護します。別個のヘッダーCRCがないと、長さバイトの1ビット反転によって受信側がペイロードに対して誤った数のバイトを読み込み、バイトストリームから完全に同期を失ってしまいます。ヘッダーCRCがあれば、破損したヘッダーは即座に拒否され、受信側は次の同期ワードを再スキャンします。そしてペイロードCRCはメッセージ本体を別個の関心事として保護するため、データのビット反転はフレーミングエラーと誤認されるのではなく、破損したペイロードとして報告されます。

この形式は1バイトずつ追えるほど小さく、すべてのパケットが同じレイアウト(同期、ヘッダー、ペイロード、CRCの順)を持つという事実は、手書きのパーサーが画面1枚分のコードに収まることを意味します。だからこそ、C、Python、Rustによる小さなホスト実装は週末プロジェクトで済むのです。プロトコルライブラリは、各側でメンテナンスされているPython版にあたります。