· 

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

 

だいぶ一般的になっているBLEについて。すごく概念的なことはあちこちに書いてある。が、下から上まで(むしろ下だけ)知りたいのに、資料少なすぎ。なので自力で調べる無茶な挑戦。第7回=(続)デバイス名称の取得について 

もうよくわからんので、Seeed XIAO BLE nRF52840からではなく、Adalm-plutoからアドバタイズを撃ってみて、そしてSCAN_REQを取れないかどうか試してみる。これの何がいいかって、ADV_INDのチャンネルを固定してしまうので、SCAN_REQも固定されるってこと。
いつも通りいきなりな感じで、
  1. import matplotlib.pyplot as plt
  2. import numpy as np
  3. import scipy.signal as signal
  4. import adi
  5. # import time
  6.  
  7. # https://jp.mathworks.com/help/signal/examples/fir-gaussian-pulse-shaping-filter-design.html
  8. def gaussdesign(bt,nsymbol_in_filter=3,nsample_per_symbol=2):
  9.     t_normalized=np.linspace(-nsymbol_in_filter/2,nsymbol_in_filter/2,nsymbol_in_filter*nsample_per_symbol+1)
  10.     a=np.sqrt(np.log(2)/2)/bt
  11.     expindex=-(np.pi*t_normalized/a)**2
  12.     h=np.sqrt(np.pi/a)*np.exp(expindex)
  13.     h=h/np.sum(h)
  14.     return h
  15.  
  16. # シンボル列を引き延ばす
  17. def extend_symbol_to_simulation_sampling_rate(symbols,t_symbol,t_simulation_sampling):
  18.     # シンボル数
  19.     size_of_symbols=symbols.size
  20.     # 最後のシンボルが終わる時間
  21.     end_time=size_of_symbols*t_symbol
  22.     # 計算タイミング
  23.     t=np.arange(0,end_time,t_simulation_sampling)
  24.     # 各シンボルの開始(終了)時間
  25.     t_periods_of_symbols=np.append(0,np.arange(1,size_of_symbols,1)*t_symbol)
  26.     # 結果データ領域確保
  27.     ex_symbols=np.zeros_like(t)
  28.     # 先頭データは入れておく
  29.     ex_symbols[0]=symbols[0]
  30.     # 各シンボルについて、その期間のインデックスを取得し、データを入れ込む
  31.     for i in range(0,size_of_symbols):
  32.         ex_symbols[np.where(t>t_periods_of_symbols[i])]=symbols[i]
  33.     return ex_symbols,end_time
  34.          
  35. # よく使う変数
  36. pi=np.pi
  37. deg2rad=pi/180.0
  38. twopi=2*pi
  39.  
  40. # 設定
  41. f_carrier=2402e6 # 搬送波周波数 in Hz
  42. f_sym=1e6 # シンボルレート in Hz
  43. m=0.5 # 変調指数
  44. f_samp_simulation=f_sym*8 # 計算のサンプリングレート in Hz
  45. thetaIB=0*deg2rad # 搬送波の初期位相(何でもいい) in rad
  46. f_deviation=(m*f_sym)/2 # 周波数偏差 in Hz
  47. n_symbol_preamble=16
  48.  
  49. # ガウスフィルタ設定
  50. n_sample_per_symbol=f_samp_simulation/f_sym
  51. BT=0.5
  52. n_symbol_in_filter=float(6)
  53. filter_length=n_symbol_in_filter*n_sample_per_symbol+1
  54. h=gaussdesign(BT,int(n_symbol_in_filter),int(n_sample_per_symbol))
  55. symbols_gf_in_base=np.zeros(int(np.ceil(n_symbol_in_filter/2)))
  56.  
  57. # シンボル列生成
  58. symbols_in_base=np.array([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,1,1,1,0,1,1,0,1,1,1,1,1,0,0,1,0,0,0,1,1,0,0,0,1,0,0,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,0,1,1,1,1,1,1,0,1,1,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,0,1,0,0,1,0,1,0,1,0,0,1,0,1,1,0,0,1,1,1,0,1,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,0,0,1,0,1,0,0,0,0,1,0,1,1,1,1,0,0,1,1,1,0,1,1,1,0,0,1,1,1,1,0,1,0,1,0,1,0,1,0,1,1,0,1,0,0,0,1,0,1,0,0,1,1,0,1,1,1,1,1,1,0,1,0,1,0,0,0,0,1,1,1,0,0,0,1,0,0,0,0,1,0,0,1,0,0,1,1,1,1,1,0,0,0,1,1,1,1,0,1,1,1,0,0,1,1,0,1,1,1,1,0,1,0,0,0,1,0,0,0,1,1,1,1,0,0,1,1,1,0,0,1,0,1,0,0,1,0,1,0,1,0,0,0,0,0,1,1,1,1,1,0,1,1,0,1,0,1,1,0,0,0,0,0,1,0,0,1,1,0,0,0,1,1,0,1,0,1,1,0,0,1,0,0,0,0,1,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0])
  59. symbols_in_base=symbols_in_base*(2)-1 # -1 or +1 にする
  60.  
  61. # 後で使う変数
  62. t_sym=1/f_sym
  63. t_samp_simulation=1/f_samp_simulation
  64. omega_deviation=twopi*f_deviation
  65.  
  66. # シンボルを計算用に拡張
  67. symbols,t_end=extend_symbol_to_simulation_sampling_rate(symbols_in_base,t_sym,t_samp_simulation)
  68.  
  69. # ガウスフィルタ設定
  70. n_sample_per_symbol=f_samp_simulation/f_sym
  71. BT=0.5
  72. n_symbol_in_filter=float(6)
  73. filter_length=n_symbol_in_filter*n_sample_per_symbol+1
  74. h=gaussdesign(BT,int(n_symbol_in_filter),int(n_sample_per_symbol))
  75. symbols_gf_in_base=np.zeros(int(np.ceil(n_symbol_in_filter/2)))
  76. symbols_gf_in_base=symbols_gf_in_base*(2)-1 # -1 or +1 にする
  77.  
  78. symbols_gf,t_end_gf=extend_symbol_to_simulation_sampling_rate(symbols_gf_in_base,t_sym,t_samp_simulation)
  79.  
  80. # ガウスフィルタ適用
  81. symbols_gf=signal.lfilter(h,1,np.append(symbols,symbols_gf))
  82. symbols=symbols_gf[symbols_gf.size-symbols.size::1]
  83.  
  84. # 計算するタイミング
  85. t=np.arange(0,t_end,t_samp_simulation)
  86.  
  87. # 全計算タイミングにおける瞬間的な角速度
  88. omega_in_moment=omega_deviation*symbols
  89.  
  90. # 全計算タイミングにおける瞬間的な位相変化量
  91. phase_change_in_moment=omega_in_moment*t_samp_simulation
  92.  
  93. # 全計算タイミングでの位相
  94. phiIB_t=np.cumsum(phase_change_in_moment)+thetaIB
  95.  
  96. # 送信波形生成
  97. xITX_t=np.exp(1j*phiIB_t)
  98. xITX_t=np.append(xITX_t,np.zeros(600000))
  99.  
  100. n_samples_rx=xITX_t.size*4
  101.  
  102. # ADALM-PLUTOの設定
  103. sdr = adi.Pluto("ip:192.168.2.1")
  104. sdr.tx_lo=int(f_carrier)
  105. sdr.tx_rf_bandwidth=int(f_deviation*8)
  106. sdr.tx_hardwaregain_chan0=0
  107. # 繰り返し送信を有効にする
  108. sdr.tx_cyclic_buffer=1
  109.  
  110. sdr.rx_lo=int(f_carrier)
  111. sdr.rx_rf_bandwidth=int(f_deviation*8)
  112. sdr.gain_control_mode_chan0='manual'
  113. sdr.rx_hardwaregain_chan0=6
  114. sdr.rx_buffer_size=n_samples_rx
  115.  
  116. sdr.sample_rate=int(f_samp_simulation)
  117.  
  118. sdr.tx(xITX_t*16384)
  119. # 受信
  120. xIRX_t = sdr.rx()
  121. print(sdr)
  122.  
  123. #sdr.tx_destroy_buffer()
  124. #sdr.tx_cyclic_buffer=0
  125. ## オブジェクト解放
  126. #sdr=None
  127.  
  128. # 復調
  129. xIRX_t0=xIRX_t[:-1:]
  130. xIRX_t1=xIRX_t[1::]
  131. symbols_rx=1*np.angle(xIRX_t1/xIRX_t0)
  132. symbols_rx=np.append(0,symbols_rx)
  133.  
  134. # 規格化
  135. symbols_rx=symbols_rx/(f_sym/f_samp_simulation*pi*m)
  136.  
  137. # プロット
  138. fig = plt.figure(figsize=(8.0, 8.0))
  139. plt.subplot(4,1,1)
  140. plt.plot(xITX_t.real)
  141. plt.plot(xITX_t.imag)
  142. plt.subplot(4,1,2)
  143. plt.plot(symbols)
  144. plt.subplot(4,1,3)
  145. plt.plot(xIRX_t.real)
  146. plt.plot(xIRX_t.imag)
  147. plt.subplot(4,1,4)
  148. plt.plot(symbols_rx)
  149. plt.show()
  150.  
  151. sdr.tx_destroy_buffer()
  152. sdr.tx_cyclic_buffer=0
  153. # オブジェクト解放
  154. sdr=None
  155. np.save('o_t',t)
  156. np.save('o_rx_t',xIRX_t)
Adalm-plutoで受信して復号したnRF52840からのデータをそのまま、Adalm-plutoで送ってみる。いちおう、GFSKにせんといかんかもしれないので、ガウスフィルタも実装している。そすっと、こうなる
上2つが送信波形とそれを作ったベースバンドで、下2つが受信波形と復調結果(それらしく見える範囲を拡大している)。
で、
Wiresharkで読み取れたデータがこちら。BeaconになってもらっていたSeeed XIAO BLE nRF52840はUSBケーブルを抜いてあるので、これがAdalm-plutoから送信されたものであるはず。でちゃんと読み取れている。で、スマホでnRF Connectを起動してSCAN状態でやってみると、
拡大してみると、
それらしいのがいる。左(先のタイミング)がAdalm-plutoからのADV_INDで右(後のタイミング)がスマホからのSCAN_REQと思われる。

Wiresharkだと
これが、Adalm-plutoが出しているやつで
これがスマホが出しているSCAN_REQっぽい。
で、Adalm-pluto波形を解析してみる、、、で、解析するコードに間違いを発見。Preamble+Access Addressをパタン検索する際に、いきなりマッチしてカウント0で戻る場合があるのに、0より大きい場合に見つかったものとしていたorzので修正。ついでに電波を送受するPythonコードで、tについて、Tx側のtを保存してしまっていたので、受信データに合わせて再生成するようにパッチあてした。
  1. import numpy as np
  2. import matplotlib.pyplot as plt
  3. from scipy import signal
  4.  
  5. def search_index_by_pattern(data,pattern):
  6.     ds=data.size
  7.     ps=pattern.size
  8.     cyc=ds-ps+1
  9.     ret=-1
  10.     if cyc>0:
  11.         for i in range(cyc):
  12.             data_part=data[i:i+ps]
  13.             if np.all(data_part==pattern):
  14.                 ret=i
  15.                 break
  16.     return ret
  17.  
  18. f_sym=1e6 #
  19. rx_all=np.load('o_rx_t.npy')
  20. t_all=np.load('o_t.npy')
  21. t_all=t_all-t_all[0]
  22. t_all_0=t_all[:-1:]
  23. t_all_1=t_all[1::]
  24. t_samp=np.mean(t_all_1-t_all_0)
  25. f_samp=1/t_samp
  26. print(f_samp/1e6) # should be 8MHz
  27. t_all=np.linspace(0,t_samp*rx_all.size,rx_all.size) #tの保存がイマイチだったので再生成する
  28. # フィルター係数生成
  29. freq_cutoff=f_sym*2
  30. w_cutoff=freq_cutoff/(f_samp/2)
  31. b,a=signal.butter(1,w_cutoff,'lowpass')
  32.  
  33. # フィルター適用
  34. rx_all=signal.lfilter(b,a,rx_all)
  35.  
  36. rx_all_amp=np.abs(rx_all)
  37. rx_all_amp_dig=np.where(rx_all_amp>5,1,0)
  38. rx_all_amp_dig_0=rx_all_amp_dig[:-1:]
  39. rx_all_amp_dig_1=rx_all_amp_dig[1::]
  40. rx_all_amp_dig_10=np.append(rx_all_amp_dig_1-rx_all_amp_dig_0,0)
  41. rx_all_amp_dig_rising_edge=np.where(rx_all_amp_dig_10>0)[0]
  42. rx_all_amp_dig_falling_edge=np.where(rx_all_amp_dig_10<0)[0]
  43. print(rx_all_amp_dig_rising_edge.size)
  44. print(rx_all_amp_dig_falling_edge.size)
  45.  
  46. pa_and_aa=np.array([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])
  47. pnum=0
  48.  
  49. for i in range(rx_all_amp_dig_rising_edge.size):
  50.     if rx_all_amp_dig_falling_edge[i]-rx_all_amp_dig_rising_edge[i]>40:
  51.         rx=rx_all[rx_all_amp_dig_rising_edge[i]:rx_all_amp_dig_falling_edge[i]]
  52.         t=t_all[rx_all_amp_dig_rising_edge[i]:rx_all_amp_dig_falling_edge[i]]
  53.         # demod
  54.         rx_t0=rx[:-1:]
  55.         rx_t1=rx[1::]
  56.         rx_dem=-1*np.angle(rx_t1/rx_t0)
  57.         rx_dem=np.append(0,rx_dem)
  58.         # normalization
  59.         rx_dem=rx_dem/(f_sym/f_samp*np.pi*1)
  60.         # digitization
  61.         rx_dig=np.where(rx_dem>0,0,1)
  62.         # counter top value
  63.         counter_top=8-1
  64.         counter_half=int(counter_top/2)
  65.         cnt_list=[]
  66.         cnt_list.append(int(0))
  67.         for i in range(rx_dig.size-1):
  68.             dig_pres=rx_dig[i+1]
  69.             dig_prev=rx_dig[i]
  70.             if (dig_pres!=dig_prev):
  71.                 cnt_list.append(int(0))
  72.             else:
  73.                 if (cnt_list[i]<counter_top):
  74.                     cnt_list.append(cnt_list[i]+int(1))
  75.                 else:
  76.                     cnt_list.append(int(0))
  77.         cnt=np.array(cnt_list)
  78.         smp_pos=np.where(cnt==counter_half)[0]
  79.         rd_dig_smp=rx_dig[smp_pos]
  80.         fig=plt.figure()
  81.         fig.add_subplot(3,1,1)
  82.         plt.plot(t,np.real(rx))
  83.         plt.plot(t,np.imag(rx))
  84.         fig.add_subplot(3,1,2)
  85.         plt.plot(t,rx_dem)
  86.         plt.ylim(-1,1)
  87.         fig.add_subplot(3,1,3)
  88.         plt.plot(rx_dig)
  89.         plt.plot(smp_pos,rd_dig_smp,linestyle='',marker='.')
  90.         plt.ylim(-0.5,1.5)
  91.         fig.tight_layout()
  92.         plt.savefig('pic'+str(pnum)+'.png')
  93.         pnum=pnum+1
  94.         #plt.show()
  95.         pp_and_aa_pos=search_index_by_pattern(rd_dig_smp,pa_and_aa)
  96.         if(pp_and_aa_pos>=0): #間違ってた
  97.             print("t_start=",end="")
  98.             print(t[0])
  99.             print("t_end=",end="")
  100.             print(t[-1])
  101.             print(pp_and_aa_pos)
  102.             rd_dig_smp=rd_dig_smp[pp_and_aa_pos:]
  103.             pos=0
  104.             while pos<rd_dig_smp.size:
  105.                 for n in range(8):
  106.                     print(rd_dig_smp[pos],end='')
  107.                     pos=pos+1
  108.                     if pos==rd_dig_smp.size:
  109.                         break
  110.                 print("")
  111. fig=plt.figure()
  112. fig.add_subplot(1,1,1)
  113. plt.plot(t_all,np.real(rx_all))
  114. plt.plot(t_all,np.imag(rx_all))
  115. plt.show()
で、結果を解析すると、
6024DE30CD58ABDF0201061AFF590002150112233445566778899AABBCCDDEEFF0DEADBEEFCA54545E
と、
C30C6EB7A13FEA5BDE30CD58ABDF6458BD
が得られる。
前者はよく見たパタン(Adalm-plutoで送信したやつ)。で、後者の方、
Header : C3 (SCAN_REQ, TxAdd: Random, RxAdd: Random)
PDU Length : 0C (12bytes)
AdvA : 6EB7A13FEA5B (5B:EA:3F:A1:B7:6E)
ScanAdr : DE30CD58ABDF (DF:AB:58:CD:30:DE)
CRC : 6458BD
って読み解ける。Adalm-plutoが出したADV_INDに対してSCAN_REQを出していることがわかる(AdvAがWiresharkのデータと違うのはAdalm-plutoでうまく受信させるために何度もやり直した(スマホのSCANボタンを押した)、で、TxAddがRandomだからだと思う)。
ではnRF52840でやってみる。が、3つのアドバタイズチャンネルをAdalm-plutoで同時に受信できないので、やっぱり固定にしたい。で、
~/.arduino15/packages/Seeeduino/hardware/nrf52/1.1.8/libraries/Bluefruit52Lib/src/BLEAdvertising.cpp
をいじる。349行目にある(バージョンによって違うかも)、
.channel_mask  = { 0, 0, 0, 0, 0}        , // 40 channel, set 1 to disable
.channel_mask  = { 0, 0, 0, 0, 0xC0 }        , // 40 channel, set 1 to disable
にすれば、Channel37だけになるっぽい。この設定がChannel indexなのかRF channelなのか正直わからんのだけど、どうやらChannel indexなんやないかな?知らんけど。Wiresharkによると、実際のところこれでChannel Index : 37だらけになる。
で、さらに、
Bluefruit.Advertising.setInterval(160, 160);    // in unit of 0.625 ms
の部分も
Bluefruit.Advertising.setInterval(64, 64);    // in unit of 0.625 ms
ってすることで、Adalm-plutoでnRF52840が出したものなのかどうかをわかりやすくしてみる(高頻度で出しているやつがこいつだってわかる)。で、Adalm-plutoで受信すると、こうなる。
で、こいつを解析するんだが、ビットで出してLibreOfficeCalcで結果を算出するのがめんどくさくなってきたので、もうここまで解析用のPythonでやってしまう。で、こんなコードで、
  1. import numpy as np
  2. import matplotlib.pyplot as plt
  3. from scipy import signal
  4.  
  5. def byte_invert_order(src:np.uint8):
  6.     a=((src>>4)&0x0F)|((0xF0)&(src<<4))
  7.     b=((a>>2)&0x33)|((a<<2)&0xCC)
  8.     return ((b>>1)&0x55)|((b<<1)&0xAA)
  9.  
  10. def search_index_by_pattern(data,pattern):
  11.     ds=data.size
  12.     ps=pattern.size
  13.     cyc=ds-ps+1
  14.     ret=-1
  15.     if cyc>0:
  16.         for i in range(cyc):
  17.             data_part=data[i:i+ps]
  18.             if np.all(data_part==pattern):
  19.                 ret=i
  20.                 break
  21.     return ret
  22.  
  23. ch37whiten=np.array([
  24. 0xB1,0x4B,0xEA,0x85,0xBC,0xE5,0x66,0x0D,
  25. 0xAE,0x8C,0x88,0x12,0x69,0xEE,0x1F,0xC7,
  26. 0x62,0x97,0xD5,0x0B,0x79,0xCA,0xCC,0x1B,
  27. 0x5D,0x19,0x10,0x24,0xD3,0xDC,0x3F,0x8E,
  28. 0xC5,0x2F,0xAA,0x16,0xF3,0x95,0x98,0x36,
  29. 0xBA,0x32,0x20,0x49,0xA7,0xB8,0x7F,0x1D,
  30. 0x8A,0x5F,0x54,0x2D,0xE7,0x2B,0x30,0x6D,
  31. 0x74,0x64,0x40,0x93,0x4F,0x70,0xFE,0x3B,
  32. 0x14,0xBE,0xA8,0x5B,0xCE,0x56,0x60,0xDA,
  33. 0xE8,0xC8,0x81,0x26,0x9E,0xE1,0xFC,0x76,
  34. 0x29,0x7D,0x50,0xB7,0x9C,0xAC,0xC1,0xB5,
  35. 0xD1,0x91,0x02,0x4D,0x3D,0xC3,0xF8,0xEC,
  36. 0x52,0xFA,0xA1,0x6F,0x39,0x59,0x83,0x6B,
  37. 0xA3,0x22,0x04,0x9A,0x7B,0x87,0xF1,0xD8,
  38. 0xA5,0xF5,0x42,0xDE,0x72,0xB3,0x06,0xD7,
  39. 0x46,0x44,0x09,0x34,0xF7,0x0F,0xE3,0xB1
  40. ])
  41. f_sym=1e6 #
  42. rx_all=np.load('o_rx_t.npy')
  43. t_all=np.load('o_t.npy')
  44. t_all=t_all-t_all[0]
  45. t_all_0=t_all[:-1:]
  46. t_all_1=t_all[1::]
  47. t_samp=np.mean(t_all_1-t_all_0)
  48. f_samp=1/t_samp
  49. print(f_samp/1e6) # should be 8MHz
  50. #t_all=np.linspace(0,t_samp*rx_all.size,rx_all.size)
  51. # フィルター係数生成
  52. freq_cutoff=f_sym*2
  53. w_cutoff=freq_cutoff/(f_samp/2)
  54. b,a=signal.butter(1,w_cutoff,'lowpass')
  55.  
  56. # フィルター適用
  57. rx_all=signal.lfilter(b,a,rx_all)
  58.  
  59. rx_all_amp=np.abs(rx_all)
  60. rx_all_amp_dig=np.where(rx_all_amp>5,1,0)
  61. rx_all_amp_dig_0=rx_all_amp_dig[:-1:]
  62. rx_all_amp_dig_1=rx_all_amp_dig[1::]
  63. rx_all_amp_dig_10=np.append(rx_all_amp_dig_1-rx_all_amp_dig_0,0)
  64. rx_all_amp_dig_rising_edge=np.where(rx_all_amp_dig_10>0)[0]
  65. rx_all_amp_dig_falling_edge=np.where(rx_all_amp_dig_10<0)[0]
  66. print(rx_all_amp_dig_rising_edge.size)
  67. print(rx_all_amp_dig_falling_edge.size)
  68.  
  69. pa_and_aa=np.array([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])
  70. pnum=0
  71.  
  72. for i in range(rx_all_amp_dig_rising_edge.size):
  73.     if rx_all_amp_dig_falling_edge[i]-rx_all_amp_dig_rising_edge[i]>40:
  74.         rx=rx_all[rx_all_amp_dig_rising_edge[i]:rx_all_amp_dig_falling_edge[i]]
  75.         t=t_all[rx_all_amp_dig_rising_edge[i]:rx_all_amp_dig_falling_edge[i]]
  76.         # demod
  77.         rx_t0=rx[:-1:]
  78.         rx_t1=rx[1::]
  79.         rx_dem=-1*np.angle(rx_t1/rx_t0)
  80.         rx_dem=np.append(0,rx_dem)
  81.         # normalization
  82.         rx_dem=rx_dem/(f_sym/f_samp*np.pi*1)
  83.         # digitization
  84.         rx_dig=np.where(rx_dem>0,0,1)
  85.         # counter top value
  86.         counter_top=8-1
  87.         counter_half=int(counter_top/2)
  88.         cnt_list=[]
  89.         cnt_list.append(int(0))
  90.         for i in range(rx_dig.size-1):
  91.             dig_pres=rx_dig[i+1]
  92.             dig_prev=rx_dig[i]
  93.             if (dig_pres!=dig_prev):
  94.                 cnt_list.append(int(0))
  95.             else:
  96.                 if (cnt_list[i]<counter_top):
  97.                     cnt_list.append(cnt_list[i]+int(1))
  98.                 else:
  99.                     cnt_list.append(int(0))
  100.         cnt=np.array(cnt_list)
  101.         smp_pos=np.where(cnt==counter_half)[0]
  102.         rd_dig_smp=rx_dig[smp_pos]
  103.         fig=plt.figure()
  104.         fig.add_subplot(3,1,1)
  105.         plt.plot(t,np.real(rx))
  106.         plt.plot(t,np.imag(rx))
  107.         fig.add_subplot(3,1,2)
  108.         plt.plot(t,rx_dem)
  109.         plt.ylim(-1,1)
  110.         fig.add_subplot(3,1,3)
  111.         plt.plot(rx_dig)
  112.         plt.plot(smp_pos,rd_dig_smp,linestyle='',marker='.')
  113.         plt.ylim(-0.5,1.5)
  114.         fig.tight_layout()
  115.         plt.savefig('pic'+str(pnum)+'.png')
  116.         pnum=pnum+1
  117.         #plt.show()
  118.         pp_and_aa_pos=search_index_by_pattern(rd_dig_smp,pa_and_aa)
  119.         if(pp_and_aa_pos>=0):
  120.             print(f"t_start={t[0]}({pp_and_aa_pos}):",end="")
  121.             rd_dig_smp=rd_dig_smp[pp_and_aa_pos:]
  122.             pos=0
  123.             bpos=0
  124.             barray_str=''
  125.             while pos<rd_dig_smp.size:
  126.                 bdata=0
  127.                 for n in range(8):
  128.                     bdata=bdata*2+rd_dig_smp[pos]
  129.                     pos=pos+1
  130.                     if pos==rd_dig_smp.size:
  131.                         break
  132.                 if bpos>4:
  133.                     bdata=byte_invert_order(bdata^ch37whiten[bpos-5])
  134.                 barray_str=barray_str+format(bdata,'02X')
  135.                 bpos=bpos+1
  136.             print(barray_str)
  137.                 
  138. fig=plt.figure()
  139. fig.add_subplot(1,1,1)
  140. plt.plot(t_all,np.real(rx_all))
  141. plt.plot(t_all,np.imag(rx_all))
  142. #plt.show()
でもって、さっきの波形を解析したら
  1. 7.9999991999999995
  2. 159
  3. 159
  4. t_start=0.0017272501727250173(14):556B7D91716024DE30CD58ABDF0201061AFF590002150112233445566778899AABBCCDDEEFF0DEADBEEFCA54545E4C
  5. t_start=0.002253000225300023(7):556B7D9171C30CA7B8B5EAC25BDE30CD58ABDFFD21AF
  6. t_start=0.002572250257225026(14):556B7D91714415DE30CD58ABDF0E095849414F206E524635323834308B3BB308
  7. t_start=0.05169388016938802(14):556B7D91716024DE30CD58ABDF0201061AFF5900029539D837CB7EF9A63FBB2C3B24D7B94D04A0A5C58E6F831C5B4C
  8. t_start=0.05222013022201302(7):556B7D9171C30CA7B8B5EAC25BDE30CD58ABDFFD21AFE9
  9. t_start=0.05253975525397553(13):556B7D91714415DE30CD58ABDF0E095849414F206E524635323834308B3BB308
  10. t_start=0.09338825933882594(14):556B7D91716024DE30CD58ABDF0201061AFF590002150112233445566778899AABBCCDDEEFF0DEADBEEFCA54545E4C
  11. t_start=0.14026701402670141(14):556B7D91716024DE30CD58ABDF0201061AFF590002150112233445566778899AABBCCDDEEFF0DEADBEEFCA54545E4C
  12. t_start=0.18584964358496436(14):556B7D91716024DE30CD58ABDF0201061AFF590002150112233445566778899AABBCCDDEEFF0DEADBEEFCA54545E4C
  13. t_start=0.23199227319922733(14):556B7D91716024DE30CD58ABDF0201061AFF590002150112233445566778899AABBCCDDEEFF0DEADBEEFCA54545E4C
  14. t_start=0.2763419026341903(13):556B7D91716024DE30CD58ABDF0201061AFF590002150112233445566778899AABBCCDDEEFF0DEADBEEFCA54545E4C
  15. t_start=0.2771869027186903(14):556B7D91714415DE30CD58ABDF0E095849414F206E524635323834308B3BB308
  16. t_start=0.3170135317013532(14):556B7D91716024DE30CD58ABDF0201061AFF590002150112233445566778899AABBCCDDEEFF0DEADBEEFCA54545E4C
  17. t_start=0.3644361614436162(14):556B7D91716024DE30CD58ABDF0201061AFF590002150112233445566778899AABBCCDDEEFF0DEADBEEFCA54545E4C
  18. t_start=0.40646579064657906(14):556B7D91716024DE30CD58ABDF0201061AFF590002150112233445566778899AABBCCDDEEFF0DEADBEEFCA54545E4C
  19. t_start=0.4069920406992041(7):556B7D9171C30CA7B8B5EAC25BDE30CD58ABDFFD21AF
  20. t_start=0.40731091573109157(15):556B7D91714415DE30CD58ABDF0E095849414F206E524635323834308B3BB308
  21. t_start=0.4502735450273545(12):556B7D91716024DE30CD58ABDF0201061AFF590002150112233445566778899AABBCCDDEEFF0DEADBEEFCA54545E4C
  22. t_start=0.45079929507992955(7):556B7D9171C30CA7B8B5EAC25BDE30CD58ABDFFD21AF
  23. t_start=0.45111829511182955(14):556B7D91714415DE30CD58ABDF0E095849414F206E524635323834308B3BB308
  24. t_start=0.4925281742528175(14):556B7D91716024DE30CD58ABDF0201061AFF590002150112233445566778899AABBCCDDEEFF0DEADBEEFCA54545E4C
  25. t_start=0.5345579284557929(13):556B7D91716024DE30CD58ABDF0201061AFF590002150112233445566778899AABBCCDDEEFF0DEADBEEFCA54545E4C
  26. t_start=0.5801895580189559(13):556B7D91716024DE30CD58ABDF0201061AFF590002150112233445566778899AABBCCDDEEFF0DEADBEEFCA54545E4C
  27. t_start=0.6218823121882312(13):556B7D91716024DE30CD58ABDF0201061AFF590002150112233445566778899AABBCCDDEEFF0DEADBEEFCA54545E4C
  28. t_start=0.6649379414937941(15):556B7D91716024DE30CD58ABDF0201061AFF590002150112233445566778899AABBCCDDEEFF0DEADBEEFCA54545E4C
  29. t_start=0.6654641915464192(7):556B7D9171C30CA7B8B5EAC25BDE30CD58ABDFFD21AFE9
  30. t_start=0.6657830665783067(13):556B7D91714415DE30CD58ABDF0E095849414F206E524635323834308B3BB308
  31. t_start=0.7054006955400696(14):556B7D91716024DE30CD58ABDF0201061AFF590002150112233445566778899AABBCCDDEEFF0DEADBEEFCA54545E4C
  32. t_start=0.749431449943145(14):556B7D91716024DE30CD58ABDF0201061AFF590002150112233445566778899AABBCCDDEEFF0DEADBEEFCA54545E4C
  33. t_start=0.7499568249956825(8):556B7D9171C30CA7B8B5EAC25BDE30CD58ABDFFD21AF
  34. t_start=0.7502764500276451(14):556B7D91714415DE30CD58ABDF0E095849414F206E524635323834308B3BB308
  35. t_start=0.7965969546596955(14):556B7D91716024DE30CD58ABDF0201061AFF590002150112233445566778899AABBCCDDEEFF0DEADBEEFCA54545E4C
  36. t_start=0.8286788328678834(7):556B7D9171C30CA7B8B5EAC25BBBAF54C78D7358B503E9
  37. t_start=0.8424845842484585(13):556B7D91716024DE30CD58ABDF0201061AFF590002150112233445566778899AABBCCDDEEFF0DEADBEEFCA54545E4C
  38. t_start=0.8898920889892089(14):556B7D91716024DE30CD58ABDF0201061AFF590002150112233445566778899AABBCCDDEEFF0DEADBEEFCA54545E4C
  39. t_start=0.890417839041784(7):556B7D9171C30CA7B8B5EAC25BDE30CD58ABDFFD21AF
  40. t_start=0.890737089073709(15):556B7D91714415DE30CD58ABDF0E095849414F206E524635323834308B3BB308
  41. t_start=0.939841468984147(14):556B7D91716024DE30CD58ABDF0201061AFF590002150112233445566778899AABBCCDDEEFF0DEADBEEFCA54545E4C
  42. t_start=0.9841122234112224(14):556B7D91716024DE30CD58ABDF0201061AFF590002150112233445566778899AABBCCDDEEFF0DEADBEEFCA54545E4C
  43. t_start=1.0294228529422853(13):556B7D91716024DE30CD58ABDF0201061AFF590002150112233445566778899AABBCCDDEEFF0DEADBEEFCA54545E4C
  44. t_start=1.0758213575821358(14):556B7D91716024DE30CD58ABDF0201061AFF590002150112233445566778899AABBCCDDEEFF0DEADBEEFCA54545E4C
  45. t_start=1.1229389872938988(14):556B7D91716024DE30CD58ABDF0201061AFF590002150112233445566778899AABBCCDDEEFF0DEADBEEFCA54545E4C
  46. t_start=1.1234643623464362(7):556B7D9171C30CA7B8B5EAC25BDE30CD58ABDFFD21AF
  47. t_start=1.1237839873783988(14):556B7D91714415DE30CD58ABDF0E095849414F206E524635323834308B3BB308
  48. t_start=1.1657067415706743(13):556B7D91716024DE30CD58ABDF0201061AFF590002150112233445566778899AABBCCDDEEFF0DEADBEEFCA54545E4C
  49. t_start=1.2126012462601248(14):556B7D91716024DE30CD58ABDF0201061AFF590002150112233445566778899AABBCCDDEEFF0DEADBEEFCA54545E4C
こうなってる。
45,46,47行に着目すると
[45行目]
60 : ADV_SCAN_IND 
24 : 36byte
DE30CD58ABDF : AdvAdr=DF:AB:58:CD:30:DE
(...省略)
[46行目]
C3 : SCA_REQ, TxAdd=Random, RxAdd=Random
0C : 12byte
A7B8B5EAC25B : ScanAdr=5B:C2:EA:B5:B8:A7 
DE30CD58ABDF : AdvAdr=DF:AB:58:CD:30:DE
[47行目]
44 : SCAN_RESP, TxAdd=Random
15 : 21byte
DE30CD58ABDF : AdvAdr=DF:AB:58:CD:30:DE
0E : 14Byte
09 : Complete Local Name
5849414F206E52463532383430 : XIAO nRF52840
よし、できた。
いちおうWiresharkによると、
おお、同じだ。
、、
、じゃぁWiresharkでいいやん、、、あ、そす、、、無線の波形からデータにしてみたかっただけです。こんなの知らんでもいいんやろうけど、モノ作りでは、うまくいかないことも多くて、そういう時に、自分で手を動かした、自分の目で見た経験が活きると思ってる。最近できてないなーーー
 
そういえばVSCodeのArduino拡張機能が廃止になったらしい。で、ArduinoIDEを使えって書いてある。Arduino IDEは1系だと変数や関数をたどるのがメンドクサイ、2系だと、、、思い出せないけど1度移行してみたもののたしか嫌な点があってやめたんだったと思う。が、実際問題、2系を使っていくしかないかな。