2008-12-30

私鉄空線のデコード(最終章?)

なんとかちまちま作ってきた私鉄空線デコーダーですが、とりあえずα版ぐらいのレベルまでたどり着けました。といっても、まだ作りこんでいないところがいくつかありますが、デコード機能的には完成したという感じです。

というわけで、現在の画面ですが、基本的にはデコードした結果を表示するだけなので地味なものですw。あと、前の記事でも書いたとおり、空線キャンセラーの機能も入っています。ただ、気がかりなところといえば、実行時のプロセッサーの負荷率がやや高いといったところでしょうか。サンプリングレートを上げて、デュアルデコード(左右チャンネル同時にデコードする)を実行すると、ちょっと重いかな?といった感じがします。

まだ、しばらくは作りこみが必要ではありますが、テストをしたいというのもあるので、限定で公開しようかと思っています。とりあえず、LiveSpaceに「関東私鉄空線研究会」というグループを作ったので、興味がある方がいらっしゃたら、WindowsLiveのアカウントを作って、入会をリクエストしてみてください。
そういったわけで、私鉄空線デコードネタは、このブログではとりあえず、クローズしたいと思います。

テストをお願いするにこちらから注文をつけるのも忍びないですが…。テストの成果をそれなりに出したいので、とりあえずの条件としては…。

  • それなりにWindowsを使いこなしている人
  • それなりに鉄道無線に精通している人
  • 空線がデコードできる私鉄の鉄道無線を受信できる人

要するにですね、いまのところヘルプとかの詳しいドキュメントができてないので、いきなり実行モジュールをもらってもなんとかなるとか、鉄道無線の受信の仕方とかを指南しなくてもなんとかなるとか、PCと受信機をオーディオケーブルでつなげられるとか、そういったレベルなので、普通に私鉄の無線を聞いている人でPCを持っていればおKといったところです。

受信電界強度が弱くて、ノイズ混じりだったり、電界強度が不安定だとデコードできないかもしれないので、必然的に特定の私鉄沿線の近くで受信できる人という条件にもなります。結局のところ関東というか首都圏の一部の地域に限定されてしまいますが…。

あと、できれば、フォーマットの解析もしたいので、いろいろな沿線の人に使ってもらいたいと思っています。このソフトは、最終的にはフリーウェアとして公開する予定なので、ボランティアで参加ということで…。そういうわけで、よろしくおねがいします。

2008-12-08

私鉄空線のデコードと空線キャンセラー

おまけの話でなんですが、空線がデコードできるということは、空線があるか否かがわかるという単純な発想で、作成中の空線デコーダーにいわゆる空線キャンセラーの機能を入れてみました。

空線キャンセラーといえば、空線がどのくらい除去されるのかというのが話題になるのですが…。
やる気になれば、デコード可能な信号という条件なら、ほぼ100%除去することも可能です(といっても、よっぽど電界強度が高く、安定してないと100%のデコードにはなりませんが…)。ただし、キャプチャと信号処理で必ず遅延が生じるので、それをするにはループバック(ラインインから取り込んで、Waveアウトに出力)させないとダメです。しかし、そこまでするのはかなり面倒なのでやりません(少なくともデコーダーの機能ではない)。

そういう意味では、簡易空線キャンセラーというべきかもしれませんが、信号処理でデータが抽出できたら、ミキサーコントロールを操作してミュートにするいう仕組みで、信号処理後にミュートですから、もちろんそれなりの遅延が生じます。

そもそも、キャプチャーはある程度のデータがバッファに溜まってから取り込むので、最悪バッファ長に相当する時間の遅延が発生するわけです。
さらに、そこから信号処理をするわけですが、プロセッサーの処理速度に関係なく遅延するのがフィルタです。

いわゆるデジタルフィルタは、ある程度の数の連続したサンプルを使って、出力を計算するわけですから、キャプチャしたデータの最後のほうは、次の取り込みでデータを取得しないと計算できません。あと、フィルタ長にもよりますが、フィルタによる遅延も影響してきます。

あとの信号処理は、大きく遅延するような処理はしないので、プロセッサーのパワー次第です。ただし、少なくともパソコンの場合、信号処理だけで負荷が高いと結果的にキャプチャーの取りこぼしにつながるので、ほどほどになるようにチューニングしておきます。そういった意味では、そこではそれほどの遅延にはならないはずです。

といった具合で、現在の空線デコーダーに組み込んだ簡易空線キャンセラーの場合、おそらく0.3秒ぐらいの遅延が生じているようです。これは、アップリンクの1パケットがだいたい0.5秒ぐらいなので、そこから聞いた感じで推測しているので、あまり正確な数字はないですが…。

と、いいつつも空線キャンセラーの機能は、デコーダー的にはまったくのおまけなので、これ以上どうにかしようとかは思っていません…。

私鉄空線のデコーダー

私鉄空線をデコードできるということは、過去記事にていくつか述べましたが、Windows上で動くデコーダーが概ね完成しました。まだ、絵的には完成していない(アイコンとか)ので、スナップショットは、体裁が整ったらご紹介しようかと思っています。

ただ、安定して受信できるのが、西武線しかないのでフォーマットの解析は西武線のみになっています。解析はほぼ完了しておりますが、まだいくつかのなぞが残っています。詳しい人がいたら教えてほしいのですが…。

西武線の場合、無線はLCXを使っているのですが、どうやら、沿線全てで指令側が1つではなくどこかに切れ目があり、場所によって指令側が変わるようです。
おそらく、どこかの駅の下り側と上り側では指令側の担当が違っているところがあるということです。

その切れ目がどこかというのが知りたいのですが…。いまのところの予想では、西武新宿線は、田無あたりに切れ目がありそうではないかと思ってます。
これとデコードとの何の関係があるかというと、このエリアによってデータが変わるのではなかろうかということです。

もちろん、それらしき場所に移動して、受信しデコードすれば判明することなのですが…。デコードをするには、それなりに電波が安定していないとダメなので、それなりの装備が必要ではないかと…。

比較的近い西武線ですらこんな感じですから、他の私鉄はほとんど手付かずです。耳で聞く限りでは受信できるところもあるのですが、デコードとなると条件は別で、電界強度が高くてもデコードできないこともありますし、聞いた感じノイズがのっていてもデコード可能という場合もあったりします。

そのへんは、デコーダーが出来上がったら、それぞれの沿線に得意な人に使ってもらって解析するというほうが無難ですかね…。

2008-11-02

MSKをゼロクロスでデコード

MSK(CPFSKも含む)をゼロクロスを使ってデコードする方法について書いてみることにします。今回作成している私鉄空線のデコーダもこの方法を使っています。

ゼロクロスは、振幅の0をまたいだ時間を測定して、信号の周波数を測定するという方法としてよく知られている方法です。基本原理は、振幅が0を基準となる周期信号は1周期で2回符号が入れ替わる(すなわち、0をまたぐ)ということから、符号をが2回変わった時間を測定すれば、1周期の時間となりその逆数が周波数になるというものです。

MSK(CPFSK)の信号は、位相が連続して周波数が移行するといったものですが、シンボルが入れ替わるところは、一般的に振幅が0となるところになります。他には最大振幅点で変わるものもあるようですが、あまり多くはないようです。
ここでは、一般的となる振幅0で移行するもの(これをsin型という)をゼロクロスを使ってデコードする方法について書くことにします。

まずは、変調速度に同期するのですが、上記の条件の通りシンボルの入れ替わりは、常に振幅0の位置で起こるわけですから、ゼロクロスの点になります。同期の開始位置は、シンボルの入れ替わる場所となりますが、もし1つシンボルが変調速度の2倍の周波数となる場合、それだけでも、変調速度との同期が可能です。

この手の処理は、ハードウェアでは変調速度と同じクロックをPLLを使って同期させるというのは一般的な方法です。しかし、ソフトウェアでのデコードの場合、多少事情が異なります。
というのは、信号はサンプリングされてデジタル化されるわけですが、変調とは無関係なクロックでサンプリングされてしまっているので、完全な同期というのはできません。しかし、変調速度よりも比較的高い周波数でサンプリングされていれば、誤差は多少あるもののデコードはは可能です。

サンプリングされたデータからゼロクロスを検出するには、振幅が0以上か未満かで2つに分けて、1つ前のサンプルと比較して違っていれば、1つ前のサンプルとそのサンプルの間のどこかにゼロクロス点があるということになります。
そして、次のゼロクロスを見つけてその間のサンプル数を数えていけばよいわけです。ただし、サンプリングのクロックとの関係上1サンプル分の誤差が生じる可能性があるため、その分を考慮して同期をとるようにしていきます。

あとは、変調速度に相当するサンプル数(サンプリング周波数÷変調速度)毎にゼロクロスが存在することを確認しながら、その間に含まれるゼロクロスの数を数えていけばよいです。もし、変調速度と同じ周波数で変調されていれば、ゼロクロスは中間に1つ存在するわけですし、1/2の周波数ならば、ゼロクロスは0個といった具合になります。

といった方法でデコードは可能ですが、現実にはゼロクロスの位置が信号の歪みにより前後することがあります。なので、その分を考慮し多少前後しても許容するような作りのほうがデコード能力は高くなります。ただし、あまり甘くしすぎると誤検出することもあるので、その辺のさじ加減はチューニングのひとつとなるでしょう。

あと、ゼロクロスに限った話ではないですが、前処理としてフィルタをかけておくと、比較的簡単にデコード能力が向上します。もちろん、フィルタの選び方次第で良くも悪くもなるので、それなりに適切なフィルタを選ぶ必要はあります。
正直言って、ベストなフィルタというのはわかりません。基本的にはBPFをかけるのですが、通過域が広いとノイズの除去になりづらいですし、狭くしすぎると歪みが起きてゼロクロスがずれます。

ゼロクロスの処理は、アルゴリズムも比較的簡単でプロセッサの処理的には軽く実現することができるので、まずはMSKなどのデコーダを作ってみようという人にはおすすめです。
実際に作った空線のデコーダですが、上記の処理方法をベースに作成していますが、十分なデコード能力を持っていると思います。

2008-10-31

関東私鉄空線の仕様

私鉄の空線のデコードですが、大まかな仕様がわかってきたので、まとめてみます。ただ、独自に解析した結果であって、正式な仕様とは違っている可能性は十分あります。あくまでも参考ということでお願いします。

前回の記事でチェック方式は、CRC12ではなかろうかということでしたが、予告通りを総当りしたところ、生成多項式が見つかったので、CRC12で確定とします。なお、パリティはありませんでした。
あと、変調方式ですがMSKといってましたが、おそらく正しくはCPFSK(位相連続のFSK)ではなかろうかと思ったので、そう書いておきます。

変調仕様
変調方式変調速度マーク周波数スペース周波数
CPFSK1200ボー1800Hz1200Hz

初期信号、いわゆる空線時の信号は「マーク・スペース」の連続となります。空線間に送出される信号(アップリンクで一回に送られるものも同様)をとりあえずパケットということにします。

パケット仕様
初期信号フレーム初期信号フレーム初期信号フレーム初期信号

要するにパケットは、同じ内容のフレームが複数個存在しているということです。含まれるフレームの数は、可変で鉄道会社によって変わるようですし、フレームの内容によって数が増えることもあるようです。
フレーム間の信号は、基本的には8個のマーク・スペースの連続(16シンボル)となります。

フレーム仕様
同期パターンデータ終了文字CRC
16ビット可変長8ビット12ビット

データは、鉄道会社によって長さが異なるようですが、会社毎に長さは一定で4の倍数ビットとなるようです。終了文字も鉄道会社毎に異なりますが、データの長さによって決まっているかもしれません。いまのところ、異なる鉄道会社において同じデータ長を使用しているところがあるかはわかりません。
予想としては、データ長と終了文字で鉄道会社を識別できるのではないかと思っています。そういった意味では、終了文字というよりは会社識別用ビット列なのかもしれません。

データ内の仕様ですが、2つに分けられると思われます。そのうち前半は、おそらく識別符号として使われていて、この部分が全て「1」のパターンが存在しており、これは一斉呼び出しなどの相手を特定しない場合に送られると考えられます。そのため、ダウンリンクのみに存在するのではないかと思います。

データの後半は、なんだかの制御用に使用されているものだと思われます。少なくともダウンリンクとアップリンクでは、この部分のビットの立ち方が異なります。また、同じ識別符号を持つパケットが2回連続で送られているようですが、そのときに2回目には1つだけビットが増えていて、再送フラグではないかと思われます。

2008-10-28

私鉄の空線のデコード(7)

相変わらず、私鉄の空線のデコードを続けています。その後、デコード精度を上げてみたところ、西武線以外の空線もデコードできるようになり、フレームのフォーマット(プロトコル)がなんとなく見えてきました。
ただ、まだデータの収集は十分ではないので、正しいかはわかりません。

かつては、フレームは可変長ではないかということをいっていたのですが、どうやら基本的には固定長であるようです。ただし、解釈によっては1ビット増えたり減ったりしているとも思えます。
フレームの後にいわゆる空線パターンの「10」の連続があるのですが、それが固定長なのかどうかはよくわからないといった感じです。

現段階で想像されるフォーマットは以下のような感じになっています。

同期パターンデータデータ終了チェック
16bitXbit16bit13bit

このうち、「データ」の長さは鉄道会社によって違うようですが、もしかしたら複数の長さのパターンがあるかもしれません。あと、いずれの場合も長さは4の倍数になっているようです。
「データ終了」のパターンも会社によって異なるように思えます。

あと、チェックだと思われるビット列ですが12bitかもしれません。よくはわからないのですが、パリティで1bit付加されるような仕様なのかもしれません。ここだけ奇数なのはやはりあやしいので、パリティではないかという予想も含まれていますが。
12bitが何かのチェックではないかというのは、以前も書いたのですがここだけがかなりランダムのようなビット配列になります。ただ、12bitのチェックアルゴリズムというと、CRCぐらしか思いつかないのですが、生成多項式がわからないので確認はしていません。
ちなみにネットで調べたところによると12bitのCRCというがあるらしいです。ただし、どこが規定したものかはわかりませんでした。12bitなら、総当りで計算してもたいしたことはないので、最悪それで見つけてみようかとは思っています。

2008-10-12

私鉄の空線のデコード(6)

関東私鉄の空線のデコードの続きですが、フレームのサイズが変わるのが気になったので、ビットの同期を少し精度を上げてみたのですが、やはり違ったサイズのフレームが送られているように思われます。
1ビット違うというのであれば、デコードを失敗しているのではないかとも思うのですが、サイズが3分の2ぐらいのものがあるので、さすがにそこまでは失敗はしないはずなので、やはり可変長なフレームになっているのではないでしょうか。

といったわけで、これから先の解析はデータの収集が必要ではなかろうかと思いつつ、面倒なので、とりあえずこのへんでいったん中断しようかと思います。ただ、作ったソフトが中途半端だと再開したときに訳がわからなくなるので、デコードしたデータを表示するだけという、かなり寂しげなソフトですが、いちおうそれなりにデコードはできるので、形だけということで現状版としてFIXしてしまおうかと思っています。

あえてスナップショットを見せるほどのものではありませんが、折角なので画像をアップしておきます。画像をそのままアップすると例の電波法に引っかかると叩かれそうなので、一部モザイクをかけました。一部でいいのか?という気もしますが…。

デコードしたのは、西武線のアップリンクです。データの中身の意味はわかりませんが、フレームの前半は比較的規則性のありそうな数字の並びになってますが、後半の16ビットぐらいはどちらかというとランダムな感じのする数字になってます(モザイクで見えないですが)。
そういう意味から推測すると、後半は何かエラーチェック用のデータであると思います。16ビットで間違えがなければCRC16かもしれませんが、そうでなければBCHかもしれません。前半のデータが何も加工されずに送られているのであれば、エラーチェックは無視しても問題はないとは思うので、ある程度の解析はできるのかもしれません。

2008-09-28

私鉄の空線のデコード(5)

私鉄の空線のデコード(アップリンクも含む)の解析として西武線をデコードしていますが、データのパターンがいろいろあるようなので、手間取っています。

まず、フレームの長さが一定ではないということがわかりました。ダウンリンクはほとんどが56ビットですが、アップリンクは57ビットになります。ただし、両者ともビット長が短いフレームやまれに長いものも存在するようです(長いほうはデコードエラーかもしれないが…)。
特にダウンリンクのほうは、短いフレームを送っている頻度が高いように思えます。

あと、フレームの長さとデータとの間になんだかの相関関係があるようで、先頭の数ビットにパターンで長さが変わる傾向があります。ただし、そこにデータの長さを示す数値があるようには見えません。おそらく、データの種類を識別する情報が先頭に存在するのではないかと推測されます。

あとは、データの中に共通するビットパターンが存在するようです。ただし、それほど多くのデータを見たわけではないので、フレームに含まれる情報が同一のものであるかもしれません。頻度の低いデータの場合、このパターンが含まれない可能性もあります。

と、いった具合で、思ったよりも複雑なため、これから先は解析が難しそうです。もっと大量のデータを収集してパターンを整理しなければならないかもしれません。

2008-09-23

私鉄の空線のデコード(4)

私鉄空線のデコードですが、その後プログラム的にデータを分割してみたところ、前回の記事の内容と違っているところがあったので、一部訂正します。
といっても、まだ解析が十分とはいえないので、この先また違ったことになっているかもしれません…。

まずは、複数送信されるデータ(これをフレームとする)は、基本的には102ビット(13バイト)になっているようです。ただし、たまに100ビットになっているときもあるようなので、フレームは可変長かもしれません。

あと、一度に送信されるフレームの回数ですが、基本的には7回送信しているようです。ただし、たまに聞いた感じでも通常より長い時間送っている場合があるので、なんだかの情報を送ろうとしているときには、1回に送る回数を増やしているようです。
ただ、これはアップリンクを受信して確認したものなので、ダウンリンク側の空線ではこのようなパターンがないかもしれません。確認はしてませんが、ダウンリンクで通話が始まる前に送出されているデータがこの回数の多いパターンかもしれません。
ということを仮定すると、アップリンク側の回数の多いデータは、通話を要求しているときに送られるものとか、重要な情報を送るときのパターンかもしれません。

2008-09-22

私鉄の空線のデコード(3)

私鉄の空線のデコードですが、とりあえずアップリンクをいくつかデコードしてみたところ、1回の送信で同じ内容のデータを複数回送っているもよう。今回も西武線を受信しているが、他の私鉄も同じような感じに聞こえるため、おそらく同様なことをしていると思われる(ちゃんとデコードできてないので未確認)。

ちなみに具体的には、80ビットの同じ内容のデータを複数回(回数はちゃんと確認してないが、8回ぐらいと思われる)を送信しているようにみえる。さらに最初の16ビットと最後の16ビットは内容が固定されているようなので、実質48ビット(6バイト)のデータになる。

同じ内容のデータを続けて送っているということを考えると、何かエラー訂正をしているようではなさそうだと思われる。実際に8ビットごとにパリティをチェックしても同じにならない。エラー訂正的には、べたな方法だがエラーチェックは単純にデータの比較だけで済むので、処理は楽かもしれない。

この私鉄無線のデータ通信をどのようなことに使っているかはわからないが、単純にセルコールとして利用しているのであれば、発信元か呼び出し先があれば十分なはずなので、6バイトというデータ量でも用は足りているのかもしれない。基本的に全2重な通信なので、アップリンクでは発信元、ダウンリンクは呼び出し先(一斉コールも含めて)でよいとすれば、4バイトでも十分かもしれない。あとは何かの制御用の情報とも考えられる。

2008-09-20

私鉄の空線のデコード(2)

前回、私鉄空線のデコーダを試しに作ってみたということを書いたが、やはりアルゴリズム的にやっつけだったためビットを拾えなかったので、ZeroCrossベースでMSKデコーダを新たに作ってみた。
空線は入力電界が不安定なためエラーが多かったが、アップリンクを受信してみたところ、比較的強力で安定していたためこちらをデコードしてみることにした。

アップリンクのほうは、連続した信号ではなく単発でパケットのような信号をおおよそ0.5秒ぐらいの長さで送信しているもよう。この信号を今回作ったデコーダで、MSK1200ボー1200/1800Hzでデコードしてみたところ、600ビット以上連続でデコードできているようなので、ビットシンクはうまくいっていると思われる。もちろん、入力電界が弱い信号は途中でエラーとなってしまうが…。

ダウンリンク(空線があるほう)は、一定な音との合間に定期的に送っている信号や通話の前や通話中に送られている信号もアップリンクの信号に似ているので、おそらくこのデコーダで解読できるかもしれない。
といっても、この先の仕様はまったくわからないので、試行錯誤するしかないだろう。とりあえず、1フレームを8ビットとかで仮定して、先頭のビットパターンを見つけ出すとかしかないかもしれない。無信号のときのビットパターンが01の繰り返しなので、NRZIになっているかもしれないし、調べることはいっぱいありそう…。

2008-08-27

私鉄の空線のデコード

私鉄といっても関東とからしいが、鉄道無線に使われている通話以外のときの音(いわゆる空線 信号)は、MSKらしいのでデコードしてみようと試みた。
仕様的には、1200ボーで1200/1800Hzの信号ということらしいので、やっつけでMSKデコーダをつくってみた。

うちで聞こえる私鉄の無線だと西武線だったので、デコードしてみたらビットシンクに失敗してデコード不能w。やはり、FM変調で入力電界が低いとノイズにやられてデコードが難しいっぽい。振幅の変動と周波数の高いノイズの対策をしなくてはならないのであろうか…。

とりあえず、3kHzぐらいのLPFでも入れてみて再度試してみる予定。あと、できれば制御用のデータの仕様も知りたいけど、当然公開はされてないだろうから地道に探るしかないのかもしれない。せめて、フレーム長とパリティとかCRCとかがわかっていると楽なんだけどね。

2008-05-22

CRC-CCITTの計算の高速化

ずいぶん前の話だが、CRC-CCITTの計算という記事を書いて、とてつもなくシンプルなソースを出していたのだが、もう少し使えそうな方法として、テーブルを使って計算を高速化する方法を書いてみる。

そのときの記事のソースコードを見てもらえればおわかりになると思うが、計算されるCRCの値は、入力されたデーターのビットとXORして、その結果から多項式のビットとXORをしているというものである。
CRCの値は、XORの計算のみであるため、結合則((A xor B) xor C = A xor (B xor C))が成り立つので事前に計算しておくことができる。実際には、初期値0で8ビット分(0~0xFF)の多項式の計算しておき、その結果をテーブルに入れておいて、データーと現在のCRC値の下位8ビットからテーブルのインデックスを割り出し、その結果とCRC値の上位8bitを下位にシフトしたものとXORすればよい。

といった、説明をされても、ピンと来るとは到底思えないので、実際のコードをあげてみる。今回はJavaのクラスにしてみたが、Javaのjava.util.zipというパッケージの中にCRC32というクラスがある。ZIPの仕様はよく知らないのでわからないが、なんだかの初期値と多項式で32ビットのCRCを計算していると思われる。
このパッケージには、checksumというインターフェースがあるので、これを実装することにする。

import java.util.zip.Checksum;

/**
 * Compute CRC16(CRC-CCITT) for a data stream.
 * 
 * @author finky
 * @version 1.0
 * @see java.util.zip.Checksum
 */
public class CRC16 implements Checksum {
 static final int INITIAL_VALUE = 0xffff;
 static final int POLYNOMIAL = 0x8408;
 static final int table[] = new int[256];
 static {
  for (int i=0; i < 256; i++) {
   table[i] = calculate(i, 0);
  }
 }
 private int value;
 /**
  * Creates a new CRC16 object.
  */
 public CRC16() {
  value = INITIAL_VALUE;
 }
 /**
  * Calculate CRC16 for simple algorithm.
  * @param data effective 8bits.
  * @param value CRC value.
  * @return updated CRC16 value.
  */
 private static int calculate(int data, int value) {
  for (int i = 0; i < 8; i++) {
   boolean flag = ((value ^ data) & 0x0001) != 0;
   value >>= 1;
   if (flag) value ^= POLYNOMIAL;
   data >>= 1;
  }
  return value;
 }
 /**
  * Returns CRC16 value.
  * @see java.util.zip.Checksum#getValue()
  */
 public long getValue() {
  // TODO Auto-generated method stub
  return value;
 }

 /**
  * Resets CRC16 to initial value.
  * @see java.util.zip.Checksum#reset()
  */
 public void reset() {
  // TODO Auto-generated method stub
  value = INITIAL_VALUE;
 }

 /**
  * Updates checksum with specified array of bytes.
  * @see java.util.zip.Checksum#update(int)
  */
 public void update(int b) {
  // TODO Auto-generated method stub
  int index = (value ^ b) & 0x00ff;
  value = table[index] ^ (value >> 8);
 }

 /**
  * Updates CRC16 with specified array of bytes.
  * @see java.util.zip.Checksum#update(byte[], int, int)
  */
 public void update(byte[] b, int off, int len) {
  // TODO Auto-generated method stub
  for (int i = 0; i < len; i++) {
   update(b[off + i]);
  }
 }
}

javadoc用のコメントの一部が、JDKのドキュメントのパクリっぽくなっていますが、そのへんはご愛嬌ということでお願いしますw。サンプルソースのつもりなので、適当に使いまわしてもらってかまいませんが、確実に動作するか否かは保障できません(自分的にはちゃんと動いていると思うけど)。