· 

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

だいぶ一般的になっているBLEについて。すごく概念的なことはあちこちに書いてある。が、下から上まで(むしろ下だけ)知りたいのに、資料少なすぎ。なので自力で調べる無茶な挑戦。第1回=アドバタイズを出して、それを観測する。

本当はありとあらゆる資料を読むのが正解だとはわかっている。が、手を動かさないとぜんっぜん頭に入らない初老のおじさんなのです(ていうか、だいたいの資料が英語で、で、英語がわからんだけ๐·°(৹˃̵﹏˂̵৹)°·๐)。
まずは、WeMos D1R32を使ってアドバタイズを出してみる。WeMos D1R32については、こちら(https://sunday-engineer.jimdofree.com/2023/09/03/wemos-d1r32%E3%83%A1%E3%83%A2/)に取り上げている。で、Exampleからこいつ
を開いて書き込む。
そすっと、
こんな感じで、なにかペリフェラルとして動作していそうな感じがする。で、どんなのを出しているのかをAdalm-Plutoで観測する。Adalm-Plutoについては以前に触れてみたことがある(
ADALM-PLUTO(購入からセットアップまで)
ADALM-PLUTO(pythonで)(1)
ADALM-PLUTO(MATLABで動作確認)
ADALM-PLUTO(pythonで)(2)
ADALM-PLUTO(pythonで)(3)
)
で、まぁ気持ちを新たに、うちはLinuxなので、こちら(https://pysdr.org/content/pluto.html)を参考にしてインストール。Windowsだとこのへん(https://wiki.analog.com/resources/tools-software/linux-software/pyadi-iio)かな。通常やるっぽいんだけど、SSHでAdalm-Plutoに入って対応周波数の拡大はやっておく。で、こんなコードで動かしてみる。
  1. import numpy as np
  2. import adi
  3. import matplotlib.pyplot as plt
  4. sample_rate = 56e6 # Hz
  5. center_freq = 2402e6 # Hz
  6. num_samps = 1000000 # number of samples per call to rx()
  7. sdr = adi.Pluto("ip:192.168.2.1")
  8. sdr.sample_rate = int(sample_rate)
  9. # Config Rx
  10. sdr.rx_lo = int(center_freq)
  11. #sdr.rx_rf_bandwidth = int(sample_rate)
  12. sdr.rx_rf_bandwidth = int(2e6)
  13. sdr.rx_buffer_size = num_samps
  14. sdr.gain_control_mode_chan0 = 'manual'
  15. sdr.rx_hardwaregain_chan0 = 0.0 # dB, increase to increase the receive gain, but be careful not to saturate the ADC
  16. # Clear buffer just to be safe
  17. for i in range (0, 10):
  18.     raw_data = sdr.rx()
  19. # Receive samples
  20. rx_samples = sdr.rx()
  21. print(rx_samples)
  22. t=np.linspace(0,1/sample_rate*num_samps,num_samps)
  23. np.save('o_t',t)
  24. np.save('o_rx_t',rx_samples)
  25. # Calculate power spectral density (frequency domain version of signal)
  26. psd = np.abs(np.fft.fftshift(np.fft.fft(rx_samples)))**2
  27. psd_dB = 10*np.log10(psd)
  28. f = np.linspace(sample_rate/-2, sample_rate/2, len(psd))
  29. # Plot time domain
  30. plt.figure(0)
  31. plt.plot(np.real(rx_samples))
  32. plt.plot(np.imag(rx_samples))
  33. plt.xlabel("Time")
  34. # Plot freq domain
  35. plt.figure(1)
  36. plt.plot(f/1e6, psd_dB)
  37. plt.xlabel("Frequency [MHz]")
  38. plt.ylabel("PSD")
  39. plt.show()
まぁ、ちょうど出しているところが引っかかってくれるかどうかで、何度かトライしてみて、こんな感じになる(見たいところを拡大済み)。
搬送波周波数+/-周波数変位幅にピークが出ないのはGFSKの性質なんだろう(局初周波数=搬送波周波数で直行復調済み波形での計算結果なので0が搬送波周波数)。
で、読み取ったデータを保存するようにしてあるので、これを解析してみる。
解析は、かつてFSKを調べるときに使ったコード(https://sunday-engineer.jimdofree.com/2020/07/22/%E3%83%91%E3%83%AB%E3%82%B9%E3%81%A7%E7%9B%B4%E4%BA%A4%E5%BE%A9%E8%AA%BF-5-numpy%E3%81%A7%E3%82%84%E3%81%A3%E3%81%A6%E3%81%BF%E3%82%8B/)を参考に、こんなかんじで、
  1. import numpy as np
  2. import matplotlib.pyplot as plt
  3. from scipy import signal
  4. f_sym=1e6 #
  5. rx=np.load('o_rx_t.npy')
  6. t=np.load('o_t.npy')
  7. rx=rx[208500:228500]
  8. t=t[208500:228500]
  9. t=t-t[0]
  10. t_0=t[:-1:]
  11. t_1=t[1::]
  12. t_samp=np.mean(t_1-t_0)
  13. f_samp=1/t_samp
  14. print(f_samp/1e6) # should be 56MHz
  15. # フィルター係数生成
  16. freq_cutoff=f_sym*2
  17. w_cutoff=freq_cutoff/(f_samp/2)
  18. b,a=signal.butter(1,w_cutoff,'lowpass')
  19.  
  20. # フィルター適用
  21. rx=signal.lfilter(b,a,rx)
  22. # demod
  23. rx_t0=rx[:-1:]
  24. rx_t1=rx[1::]
  25. rx_dem=-1*np.angle(rx_t1/rx_t0)
  26. rx_dem=np.append(0,rx_dem)
  27. # normalization
  28. rx_dem=rx_dem/(f_sym/f_samp*np.pi*1)
  29. fig=plt.figure()
  30. fig.add_subplot(2,1,1)
  31. plt.plot(t*1e6,np.real(rx))
  32. plt.plot(t*1e6,np.imag(rx))
  33. fig.add_subplot(2,1,2)
  34. plt.plot(rx_dem)
  35. plt.ylim(-1,1)
  36. plt.show()
ってすると、
はて、これがなんなのか、、、まず復調波形が見にくいので2値化する。
# digitization
rx_dig=np.where(rx_dem>0,1,0)
ってのを追加してプロットするわけだ。すると
こうなる。これを拡大して波形からいってきっとNRZで読んでいけばいいんだろうとして読んでいく、、、が、何となく前から気にはなっていたけど、上のpythonコードで復調すると、正負が逆になっちゃうので、それも考慮してって、、、
ってな感じで、パワポでビットレートと思われる周期で平行線を書きまくってそれにあわせて数字を書くって方法で読み取っていったら、図中のようなコードが読み取れて、これってのが
Bluetooth Core Specificationのこれにあたるっぽいぞ。よしきた。ちなみにビット順序がひっくり返ってるやんってのは、これ
によるわけだ(と思う)。仕様書上はMSBを左に書いてあるけど、LSBファーストで送信するぜってことだと思う。
ところで、いつもイラっとするアルファベット言語圏の省略造語について、Bluetooth Core Specificationに"LIST OF ACRONYMS AND ABBREVIATIONS"っていう項があるので、ギリ許すことにする。絶望的にたくさんあるがこいつらをおぼえておかんといかん。

ところで、この実験に使っているWeMos D1R32はだいぶ古いものなので、最近のものをいくつか発注した。これ(https://akizukidenshi.com/catalog/g/g117454/)と、これ(https://akizukidenshi.com/catalog/g/g117341/)。どちらも大した値段ではないけど、無駄遣いになる可能性もあるっちゃーある。、、、投資ってそんなもんだよね。それほど大したことのない台風で秋月もお休みしたようで、まだぜんっぜん発送手配が進んでないっぽいけど。
しかし、BLEの通信を下から紐解いていくのはまぁまぁしんどい。このテーマの続編を書けるかどうか、挫折するかもしれないって心配。だいたいの人がスマホやらRaspberryPiとかで情報をとるってのはやってるんだけど、その情報がどうやって飛んできたのかってことまで掘っている記事はないんよね。今の世の中そんなものなのかな。

記事と全く関係ないけど、FusionPCBに発注したP板は昨夜OCSに持ち込まれたらしい。順調です。P板設計がごく一般的な大人のたしなみになる日も近い、、、なわけないか。

ところで、だいぶ前から気づいてはいたけど、最近はGoogle Bloggerで記事を書いて、そのソースをJimdoに貼り付けてるんだけど、改行がなぜか省略されちゃったりするんだよね。特にソースコード。メンドクサイのでJimdoように修正したりはしない。必要に応じてGoogle Bloggerで見る。