· 

BLEを下から見ていく(2)

 

だいぶ一般的になっているBLEについて。すごく概念的なことはあちこちに書いてある。が、下から上まで(むしろ下だけ)知りたいのに、資料少なすぎ。なので自力で調べる無茶な挑戦。第2回=アドバタイズ波形を復号する
さて、前回アドバタイズ波形を観測してAccess Addressっぽいの得られたので、残り全部を数字に変えていく。
今回のBLEモジュールは1Mbpsでデータを送っているはずで、サンプリングが56MS/sなので、56回に1回データを取得すればいいことになる。で、その56カウントのゼロをどこから開始するのかってのが課題となるけど、まぁ業界の人はささっとやっちゃうんだろう。で、方針は
・適当にカウントを初めて、エッジが来たところでカウンタをゼロにする
・カウンタがTOP値になったらカウンタをゼロにする
・カウンタのTOP値は56-1なので(ゼロもカウントするから)、その半分の値int((56-1)/2)で値を保存する※
(※実際はint((56-1)/2)になる配列番号を抽出して、その番号の値を取り出す...numpyらしいやり方)
で、コードはこんな感じ(前回のコードに対して、2値化を反転して、値を抽出するってのを追加した)
  1. import numpy as np
  2. import matplotlib.pyplot as plt
  3. from scipy import signal
  4.  
  5. f_sym=1e6 #
  6. rx=np.load('o_rx_t.npy')
  7. t=np.load('o_t.npy')
  8. #rx=rx[235500:248000]
  9. #t=t[235500:248000]
  10. #rx=rx[208500:228500]
  11. #t=t[208500:228500]
  12. rx=rx[32800:54800]
  13. t=t[32800:54800]
  14. t=t-t[0]
  15. t_0=t[:-1:]
  16. t_1=t[1::]
  17. t_samp=np.mean(t_1-t_0)
  18. f_samp=1/t_samp
  19. print(f_samp/1e6) # should be 56MHz
  20.  
  21. # フィルター係数生成
  22. freq_cutoff=f_sym*2
  23. w_cutoff=freq_cutoff/(f_samp/2)
  24. b,a=signal.butter(1,w_cutoff,'lowpass')
  25.  
  26. # フィルター適用
  27. rx=signal.lfilter(b,a,rx)
  28.  
  29. # demod
  30. rx_t0=rx[:-1:]
  31. rx_t1=rx[1::]
  32. rx_dem=-1*np.angle(rx_t1/rx_t0)
  33. rx_dem=np.append(0,rx_dem)
  34. # normalization
  35. rx_dem=rx_dem/(f_sym/f_samp*np.pi*1)
  36. # digitization
  37. rx_dig=np.where(rx_dem>0,0,1)
  38.  
  39. # counter top value
  40. counter_top=56-1
  41. counter_half=int(counter_top/2)
  42. cnt_list=[]
  43. cnt_list.append(int(0))
  44. for i in range(rx_dig.size-1):
  45.     dig_pres=rx_dig[i+1]
  46.     dig_prev=rx_dig[i]
  47.     if (dig_pres!=dig_prev):
  48.         cnt_list.append(int(0))
  49.     else:
  50.         if (cnt_list[i]<counter_top):
  51.             cnt_list.append(cnt_list[i]+int(1))
  52.         else:
  53.             cnt_list.append(int(0))
  54. cnt=np.array(cnt_list)
  55. smp_pos=np.where(cnt==counter_half)[0]
  56. rd_dig_smp=rx_dig[smp_pos]
  57. print(rd_dig_smp)
  58.  
  59. fig=plt.figure()
  60. fig.add_subplot(3,1,1)
  61. plt.plot(np.real(rx))
  62. plt.plot(np.imag(rx))
  63. fig.add_subplot(3,1,2)
  64. plt.plot(rx_dem)
  65. plt.ylim(-1,1)
  66. fig.add_subplot(3,1,3)
  67. plt.plot(rx_dig)
  68. plt.plot(smp_pos,rd_dig_smp,linestyle='',marker='.')
  69. plt.ylim(-0.5,1.5)
  70. fig.tight_layout()
  71. plt.show()
で、コンソールへの出力はこうなる
[1 1 1 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 0 1 1 0 1 0 1 1 0 1 1 1 1 1 0 1 1 0 0
 1 0 0 0 1 0 1 1 1 0 0 0 1 1 0 1 1 0 0 0 1 1 1 0 0 1 1 1 1 1 0 1 1 1 0 0 0
 1 0 0 0 0 0 1 0 0 1 1 0 0 0 0 1 1 0 1 1 1 0 0 1 1 1 0 1 1 0 0 0 0 0 0 0 1
 1 0 0 1 1 1 0 1 1 1 0 0 0 0 0 1 1 0 0 1 1 1 0 1 0 0 0 1 0 0 1 1 0 1 0 1 0
 0 0 1 0 0 1 0 0 1 1 1 1 0 0 1 0 0 1 0 1 1 0 0 1 0 0 1 0 1 1 1 0 1 0 0 0 0
 1 0 0 0 0 0 1 0 0 0 1 1 1 0 1 1 0 0 0 1 1 1 0 0 0 1 0 0 0 1 0 0 0 1 0 1 1
 0 0 1 1 0 1 1 0 1 1 1 0 1 0 1 1 0 1 1 0 1 0 1 0 0 1 0 1 1 0 0 1 1 0 0 1 0
 1 0 1 0 0 1 1 1 1 0 1 0 0 0 1 0 0 1 0 0 0 0 1 0 1 1 1 1 1 0 0 0 1 1 1 0 1
 1 1 1 1 0 0 0 1 1 0 1 1 0 0 0 1 0 1 0 0 1 0 1 1 0 1 1 0 1 0 1 0 1 0 0 0 1
 1 0 0 1 0 0 1 0 1 0 1 0 0 0 1 1 0 1 0 1 0 0]
で、グラフはこうなる
一番下のグラフが2値化した波形と、そのどの時点を値として取り込んだのかを示していて、拡大すると、こうなっている
まぁうまくいってそう。で、これをさらに8bitごとにLSBファーストで16進数化すればいいんだけど、どこまでがプリアンブルなのかとかを自動検出するコードを書くのがメンドクサイ(今回はプリアンブルとAccess Addressの相関が最大となる位置を探すだけなんだけど、それすらメンドクサイおじさんなのです)ので、この後は人力でやる。ちなみに8bitのことを1byteっていうことがあるけど、Bluetoothの世界では1octっていうらしい。byteはシステム依存で1byte=8bitとは限らないかららしい。
で、人力でしようとしていたんだけど、どうにもこうにも仕様と合わない、、、クッソー何か間違っているかもしれない。
上図の赤線部がどうもPDU Typeらしい。というのも、右側はD1R32のソフトをiBeaconに書き換えた後なので、ADV_SCAN_IND=0110になっているんだと思う。で、左はSCAN_REQ=0011なのかなと。が、それ以外がちっともわからん。新しいモジュールが来たら考える。、、、さっそく挫折しそう߹ㅁ߹)スマソ

あぁ夏休みが終わるぅ(鬱)。明日から年間最長の平常運転期間が始まるので、精神的にはしんどい。ともかく巨大地震が来なくてよかった。