2008年2月14日木曜日

NetBSDで「24時間ワンセグ野郎」(2)

さすがに平日はあまり時間が取れないので、あっと驚くような進捗があるわけではないけど、進んでいないというわけでもないので、メモがてらに書いてみることにした。Log-J200のUSBデバイスとしての感想など。

USBデバイスとしてのLog-J200
ざっくりいうとLog-J100もUOT-100も同じなんだが、どれもこれもUSBデバイスとしてみたときにはあまりいけているデバイスだとは言い難い。一言でいうなら、

  • USBデバイスのファーム設計のセオリーを見直してこい!
と、受け側のソフトを書きながら思わずデバイスに突っ込みをいれてしまうような、そんな仕様がいくつもある。たとえば、
  • 機能とエンドポイントの分離がうまくできてない、とか
  • コマンドを受理しても返答しない、という動作をするコマンドがある
  • なぜかbulk転送になっているMPEG2TSストリームの転送モード、とか
  • なぜか64Byte長で細切れな転送
とか。最初の「エンドポイントの使い方」は、endpoint0でやればよいようなデバイスの制御が、なぜかMPEG2TSストリームをやりとりしているendpoint2にのっている、とか、そのあたりが顕著な例。2つめも問題で、USBのホストコントローラ的には受理したならackを返してほしいと思っているのに、黙ってしまうのでコマンドがストールしたように見えてしまう。必ず受理される、と信じて送らないといけないのはどうかとおもう。
で、性能上問題になるのは3つめと4つめ。MPEG2TSのパケット長は188Byteで、空から約380KBps程度の速度でどんどんと情報が降ってきている。これを受け取るそばからUSBバスの上に流してあげる、というのがUSBワンセグチューナデバイスのお仕事。(たぶん)普通はこんな特性の情報を扱うためには、USBではアイソクロナス転送モードというのを使うことになっている。アイソクロナス転送にするとハンドシェイクやリトライをしないので時間制約に即した転送ができる、ので、次々に発生する事象(音とか映像とか電波とか)を扱うのに向いている。Log-J200(とその仲間たち)はなぜかMPEG2TSストリームの転送にBulkエンドポイントを採用している。Bulkエンドポイントでは転送サイズは64byteまでなので、188Byteの転送をするために少なくとも3回(J200の場合は4回)の転送要求発行しないといけない。

ちょっと計算してみるとわかるのだが、ワンセグのパケットは約1.2msくらいに一回の周期で受信される。つまり、1.2ms以内に4回のトランザクションを必ず発行するような動作をしていないと取りこぼしてしまう。
いまどきの速いコンピュータにとっては1msなんて無限な時間にみえるかもしれないが、実時間動作が苦手なUNIX系のOSではちゃんとやらないと実際に取りこぼしてしまう場合があった。

特に今回はugenをつかったユーザーランドアプリケーションとして書いてみているので、そのあたりは結構シビアだなあ、というのが感想。eggmanさんのlinuxデバイスドライバの話をみて、「やっぱりカーネルデバイスドライバのほうがかっこいいし、オーバーヘッド少なくなるからよいだろうなあ。」と思ったのは内緒だ。いつか気が向いたらkernelのデバイスドライバも書いてみよう。
でも、アイソクロナス転送のしてくれていれば、ユーザランドからさわってもこんなことは起こらないので、やっぱり設計に難がある、っていう気がしている。

ちなみに、内部バッファとかも持っていない設計になっているようで、USBデバイスへのポーリングが遅れるとデータの一部が化ける(nバイトシフトしているように見える&nバイト足りない)。最初は何で先頭にMPEG2TSの先頭バイト(0x47)が現れない場合があるんだろうか?と悩んでしまったが、USBアナライザの時間計測モードをつかってみたら、アクセス間隔がWindowsでみた場合とくらべて長いかった。
複数のkerenlへのリクエストをまとめるとか、小技を使って解決している。

Log-J200の受信感度
外付けアンテナがつくのであんまり問題にならないんだけど、受信したMPEG2TSストリームを数字で眺めていると、数パーセントの割合でエラーが出ているみたい。エラーとしては以下の3パターン。
  • そもそも受信できていない。MPEG2TSのカウンターを計測していると取れなかったことがわかる
  • MPEG2TSのトランスポートエラーインジケーターの値が1になっている。つまり、受信時に「このデータ使わないほうがいいじゃない?」と言ってきている
  • 先頭バイトが0x47じゃない。タイミング制約は満たしているはずだとは思うけど、たまに発生する
特にトランスポートエラーインジケーターは受信を開始してから200パケットくらいに全部ついているので、受信機が設定されてから正しいデータ出すまでに300msくらいはかかるってことかな?

NetBSDの24時間ワンセグ野郎の現状
とまあ、USBレベルでは取りこぼしもなく普通に受信情報を取り出せている。とりあえず受信データMPEG2TSの状態でファイルに書き出すこともできる。とまあ、そのくらい。いまは画像を眺めるというよりは、受信したストリームを数字で見ながら、ヘッダとかペイロードとかのいろいろな部分のパーサーとダンプ関数を書いてみて、「ほほー」とか言いながらにやけている状態。今日の参考資料はARIB-TR-B14(英語版)とかARIB-STD-B10(英語版)。なぜ英語かというと無料で公開されているから。(ARIBの英語版配布サイト

というわけで、外堀はほとんど埋まっているような感じですが、本家みたいな作りこみをできるまでには、まだしばらくかかりそうです。

最後に色気はないけど、動作中のテキスト出力など。ワンセグっていうことでPID=0x1fc8のパケット情報。本当になんかプログラムを書いている、ってい証拠に(なるかな?)。

== MPEG2TS header (PID:0x1fc8) -:P == Cont count:08, Flags PUSTART TRANSPRIO
== MPEG2TS header (PID:0x1fc8) -:P == Cont count:09, Flags PUSTART TRANSPRIO
== MPEG2TS header (PID:0x1fc8) -:P == Cont count:10, Flags PUSTART TRANSPRIO
== MPEG2TS header (PID:0x1fc8) -:P == Cont count:11, Flags PUSTART TRANSPRIO
== MPEG2TS header (PID:0x1fc8) -:P == Cont count:12, Flags PUSTART TRANSPRIO
== MPEG2TS header (PID:0x1fc8) -:P == Cont count:13, Flags PUSTART TRANSPRIO
== MPEG2TS header (PID:0x1fc8) -:P == Cont count:14, Flags PUSTART TRANSPRIO
== MPEG2TS header (PID:0x1fc8) -:P == Cont count:15, Flags PUSTART TRANSPRIO
== MPEG2TS header (PID:0x1fc8) -:P == Cont count:00, Flags PUSTART TRANSPRIO
== MPEG2TS header (PID:0x1fc8) -:P == Cont count:01, Flags PUSTART TRANSPRIO
== MPEG2TS header (PID:0x1fc8) -:P == Cont count:02, Flags PUSTART TRANSPRIO
== MPEG2TS header (PID:0x1fc8) -:P == Cont count:03, Flags PUSTART TRANSPRIO
== MPEG2TS header (PID:0x1fc8) -:P == Cont count:04, Flags PUSTART TRANSPRIO

0 件のコメント: