2026年7月27日月曜日

送信しないRFIDリーダーを作る 〜 SDRで電波を傍受してEPCを読む

 RFIDのリーダーは、タグに向けて電波を出し、タグからの反射(バックスキャッタ)を受け取ることでEPC(タグの識別番号)を読み取ります。では、自分では一切電波を出さず、既存のリーダーとタグのおしゃべりを横から傍受してEPCを読めるでしょうか?

答えは「読める」です。しかも学術的には passive Gen2 monitor として知られた確立した手法だそうです。今回は、SDR(Software Defined Radio)で920MHz帯のRFID通信を録音し、電波の波形を1段ずつ追いながらEPCを復号するまでを、実際の測定波形とともに紹介します。使った受信機は12bitの Airspy Mini、RFIDリーダはタカヤさんのリーダーで試しました。

タカヤさんリーダとAirSpyと別の目的で作った自作の八木アンテナ

UHF帯のRFID(EPC Gen2 / ISO 18000-63)の通信は、非対称な2方向でできています。

 下り(リーダー→タグ): リーダーが搬送波の振幅を変調してコマンドを送る。信号は強い。

 上り(タグ→リーダー): タグは自分では電波を出せないので、リーダーの搬送波を反射する量を変化させて情報を返す(バックスキャッタ)。信号はとても弱い。

傍受する側から見ると、下りは「大きな声で喋るリーダー」、上りは「その声に乗るタグの小さなささやき」みたいなもんです。両方を復号できれば、会話の全容がわかります。

インベントリ(タグ読み取り)の基本的な流れは次のとおりです:

 リーダーが Query(読み取り条件を指定)を送る

 タグが RN16(16bitの乱数)をバックスキャッタで返す

 リーダーが ACK(さっきのRN16を含む)を返す

 タグが PC + EPC + CRC16 を返す ← これがEPC本体

つまりEPCは「ACKに対するタグの応答」に入っています。ここを狙って復号すればいいわけです(理屈では)。

まず簡単な下りから見ていきます。リーダーは搬送波(連続波)の振幅を落とすことでコマンドを送ります。SDRで受けたIQ信号の振幅(包絡線)をプロットすると、そのパルス列がそのまま見えます。

きれいに搬送波が谷を作っているのが見えますね。この録音ではASK変調深さが約42dB 搬送波がほぼゼロまで落ちていて、とても読みやすい条件でした。

Gen2の下りは PIE(Pulse Interval Encoding, パルス間隔符号) という方式です。ポイントは「谷(低レベル期間)の幅は一定で、谷から次の谷までの間隔で0と1を区別する」ことです。面白いのはこの方式だと谷が続かないので、電波を送り続けることができる点です。タグ側はチップに電力を供給し続ける必要があるので、この方式が採用されています。

 data-0: 短い間隔。この基準時間長を Tari と呼びます。
 data-1: 長い間隔。Tariの1.5~2倍。

図の測定値を見ると、

 data-0(Tari)= 25μs
 data-1 = 37μs(Tariの約1.5倍)
 谷の幅 PW = 約6.3μs

Tariはリーダーが決める基準時間で、規格上は6.25~25μsの範囲です。今回はちょうど上限の25μsになってます。Tariが分かれば、あとは「間隔がTari寄りか、その1.5倍寄りか」で全ビットを0/1に落とせます。閾値はだいたい 1.25 × Tari あたりに置けば安定します。

コマンドの先頭には delimiter(約12.5μsの決まった長さの谷)があり、これが「コマンドの始まり」の目印になります。delimiterに続いてdata-0とキャリブレーション用のパルス(RTcal / TRcal)が来て、リーダーはこのRTcal・TRcalの長さでTariや上りの周波数をタグに伝えます。

こうして下りを復号すると、Query・ACK・QueryRepといったコマンドが読めるようになります。特にACKには先ほどのRN16(16bit)が丸ごと入っているので、これを抽出できれば「次にタグがEPCを返す」タイミングが分かります(理屈では)。

下りの振幅(リーダーの声)と、上りのバックスキャッタ帯域のパワー(タグのささやき)を、同じ時間軸で並べてみます。

上段がリーダーのコマンド列、下段がタグの応答です。リーダーが喋る(上段にパルス列)→ 少し間を置いてタグが答える(下段にバースト)、というターン制の対話がはっきり見えます。下段の長いバーストが、狙っているEPC応答です。

いよいよ本命の上りです。ACK直後のタグ応答を、周波数×時間のスペクトログラムで見てみます。
うはー、面白いですね。ここから読み取れることが3つあります。

搬送波(0kHz)の上下に対称なサイドバンドが出ています。バックスキャッタは搬送波の振幅変調なので、必ず上下対称に現れます。この対称性は「これは本物のタグ信号だ」という強い証拠になります。
応答の先頭に約280μsのクリーンなトーン(パイロット)があり、その後変調されたデータが続く形になっています。データ部分が PC + EPC + CRC16 です。
サイドバンドは搬送波から**±252kHz**の位置に出ています。この周波数を BLF(Backscatter Link Frequency) と呼ぶそうです。
タグはこのBLFの副搬送波(サブキャリア)を、Miller符号でデータに応じて位相反転させています。今回は1ビットが4サブキャリア周期分の Miller-4(M=4)で、ビット長 約15.9μs、データレート約63kbps。応答の長さ(約2.2ms)とビット数(PC+EPC+CRC = 128ビット)ともぴったり合います。

ハマった罠 その1  Millerスペクトルの「谷」

さて、上りを復号するにはBLF(=252kHz)を正確に知る必要があります。復調でこの周波数だけ抜き出し、ビットクロックの基準にするからです。数%ずれると128ビットの間にクロックがずれて復号が全滅します。

「スペクトルのピークを探せばBLFだろう」 そう思って最初に失敗しました。応答のスペクトルを高分解能で見ると、こうなっていたのです。
ピークが 227kHz と 277kHz の2本あり、その中間の252kHzには谷があります。実はこれがMillerサブキャリアの性質だそうで、パワースペクトルは真のBLFに谷を作り、両側に2つのピークが立つのです。素直にピークを拾うと227kHzを掴んでしまい、正しい252kHzを外して復号が一切通りませんでした。

正解は「2つのピークの中点をBLFとする」でした。252kHzを使った瞬間、すべてがカチッと噛み合いました。

Miller復号とCRC  そして罠 その2

BLFが決まれば、あとは+BLFのサイドバンドを複素ベースバンドに落とし、隣り合う半ビットの位相差を見てMillerの位相反転(=データ)を読み取ります。この差動方式は、タグの発振器が多少ドリフトしても壊れないのが利点です(安価なタグの発振器は結構ふらつくそうです)。

こうして128ビットを取り出したら、最後に CRC16 で検証します。Gen2のCRC16は生成多項式 0x1021、初期値 0xFFFF。PC + EPC に対して計算した値が、受信したCRCフィールドと一致すれば復号成功が確定します。CRCが通れば、それは間違いなく正しいEPCです。

…と言いたいところで、罠その2にハマりました。

復号では「BLFを少しずつ変え、ビット開始位置を総当りして、CRCが通る組み合わせを探す」ということをします。ところが16bitのCRCは1/65536の確率で偶然通ってしまいます。数百万通りの位置を試して「最初に通った1つ」を採用すると、偶然CRCを通過した誤読(偽陽性)を本物だと思ってしまうのです。実際、最初は「6応答すべて成功!」に見えたのに、探索範囲を変えると別のEPCが出てきて、嘘だと発覚しました。

対策はこうです。本物の復号は、BLFや開始位置を少しずらしても同じEPCを出します。つまり正解の周りには「同じEPCが通る位置のクラスタ」ができます。逆に偶然のCRC通過は孤立した1点です。そこで、少しずつ条件をずらして復号を何百回も試し、同じEPCが多数の位置で通ったもの(=クラスタ)だけを本物とみなすという投票方式にしました。これで6つのインベントリラウンドすべてが同一のEPCを約490箇所で一致し、偽陽性は1~2票で自動的に弾かれ、結果が確定しました。

オフラインで復号できたら、あとはこれをストリーム処理にするだけです。差動復調のベクトル化、BLFのロック、投票の早期終了などで高速化し、150msのチャンクを約280msで処理できるようにしました。結果、AirspyからリアルタイムでEPCを読み取るパッシブモニターが完成しました。

面白いのは、応答のサイドバンド強度(SNR)を測っておくと、それがそのままタグの方向探知に使えることです(これが八木アンテナを作った理由です)。読めたEPCの中から追いたい1個を選び、そのSNRを音のピッチに変換すれば、八木アンテナを振って「ピッチが最高になる向き」を耳で探せます。複数のタグが混在する環境でも、特定のEPCのタグだけを狙って方向を特定できるわけです。

まとめ

送信機能なしのSDR受信機だけで、

 下りのPIE(Tari基準のパルス間隔符号)を包絡線から復号し、

 上りのMillerバックスキャッタをスペクトログラムで捉え、

 BLFの「谷」とCRCの偽陽性という2つの罠を越えて、

 CRC検証済みのEPCを、リアルタイムで読み取る

ところまで到達しました。なんとまぁ、何に使うの的なものはありますが Mojix もこんなことしてんのかな?みたいな感じですね。


0 件のコメント:

コメントを投稿