2025年8月30日土曜日

NFCアンテナ(10)というかOpenEMS

 

受信側コイルに貼る磁性体シートを厚くしてみる。


磁性体シートを厚いものにするともちっと良くならないか調べてみる。前回は0.1mmだったので、今回は0.8mm。
いきなりだけど、計算が終わって(約9時間)受信コイルの等価回路は
記号 磁性体あり
t=0.8mm
磁性体あり
t=0.1mm
磁性体なし
L[nH] 732.655451 721.038592 644.510935
Rs[ohm] -3.63907987 -3.89987876 0.683139303
Cp[pF] -5.14011966 -5.81069019 1.61053898
Rp[ohm] 734.906331 674.748742 4262222.68
(Cpadd)[pF] 192 196 213
ってなかんじ。Lが増えてる。その他の定数がやっぱりおかしい;

んで、結合係数どんなもんかなってみていくと、
コイル間距離
[mm]
磁性体あり
t=0.8mm
磁性体あり
t=0.1mm
磁性体なし
2 0.1749 0.1708 0.1616
4 0.1565 0.1534 0.1448
8 0.1167 0.1144 0.1075
12 0.0833 0.0819 0.0775
16 0.0597 0.0587 0.0557
20 0.0432 0.0426 0.0403
ってことで、磁性体シートの厚さを厚くすると、すこーし通信距離が延びるけど、そんなに良いことは起こらないみたい。

2025年8月27日水曜日

NFCアンテナ(9)というかOpenEMS

受信側コイルに磁性体シートを貼るってかんじでOpenEMSで効果あるかどうかやってみる。


磁性体シートはあたかも磁束を集めるような効果があるので(たぶん)、受信側コイルの内側に配置すると通信距離が伸びるのではないか?って試み。
nfc_trx_analysis_main.mに磁性体シートを追加すると、こんな感じ
  1. function [freq s11 s21 s12 s22]=nfc_trx_analysis_main(
  2.   port_direction,
  3.   tx_coil_width,tx_coil_height,tx_coil_nturn,
  4.   rx_coil_width,rx_coil_height,rx_coil_nturn,
  5.   trx_distance,
  6.   disgeomplot
  7. )
  8.  
  9. physical_constants;
  10. unit=1e-3;
  11.  
  12. FDTD=InitFDTD('NrTS',6e6,'EndCriteria',1e-3);
  13. f0=14e6;
  14. fc=12e6;
  15. FDTD=SetGaussExcite(FDTD,f0,fc);
  16. BC={'MUR','MUR','MUR','MUR','MUR','MUR'};
  17. FDTD=SetBoundaryCond(FDTD,BC);
  18.  
  19. CSX=InitCSX();
  20. if port_direction>0
  21.   txport_excite=false;
  22.   rxport_excite=true;
  23. else
  24.   txport_excite=true;
  25.   rxport_excite=false;
  26. endif
  27. [CSX port1]=gen_small_loop_coil(
  28.   CSX,
  29.   'tx',
  30.   tx_coil_width,tx_coil_height,tx_coil_nturn,
  31.   0.2,0.4,0,
  32.   0.2,
  33.   1,1e-18,1,txport_excite,
  34.   0,0,trx_distance/2
  35. );
  36. [CSX port2]=gen_small_loop_coil(
  37.   CSX,
  38.   'rx',
  39.   rx_coil_width,rx_coil_height,rx_coil_nturn,
  40.   0.2,0.4,0,
  41.   0.2,
  42.   1,1e-18,2,rxport_excite,
  43.   0,0,-trx_distance/2
  44. );
  45.  
  46. ## magnetic material
  47. CSX = AddMaterial(CSX, 'mag_material');
  48. CSX = SetMaterialProperty(CSX, 'mag_material', 'Mue',140);
  49. x_mag=rx_coil_width/2-(rx_coil_nturn-1)*0.4-0.2-1;
  50. y_mag=rx_coil_height/2-(rx_coil_nturn-1)*0.4-0.2-1;
  51. z_mag=0.2/2;
  52. mag_origins=[0 0 -trx_distance/2];
  53. CSX=AddBox(CSX,'mag_material',1,[-x_mag -y_mag z_mag-0.05]+mag_origins,[x_mag y_mag z_mag+0.05]+mag_origins);
  54. ##
  55.  
  56. air_box=16;
  57. mesh_box_size=[20 20 16];
  58. mesh_box_resolution=16;
  59. mesh=DetectEdges(CSX);
  60. mesh.x=[-mesh_box_size(1) mesh.x +mesh_box_size(1)];
  61. mesh.y=[-mesh_box_size(2) mesh.y +mesh_box_size(2)];
  62. mesh.z=[-mesh_box_size(3) mesh.z +mesh_box_size(3)];
  63.  
  64. mesh=SmoothMesh(mesh,mesh_box_resolution);
  65.  
  66. mesh.x=[-air_box+mesh.x(1) mesh.x mesh.x(end)+air_box];
  67. mesh.y=[-air_box+mesh.y(1) mesh.y mesh.y(end)+air_box];
  68. mesh.z=[-air_box+mesh.z(1) mesh.z mesh.z(end)+air_box];
  69.  
  70. mesh=SmoothMesh(mesh,c0/(f0+fc)/unit/10,1.5,'algorithm',1);
  71.  
  72. mesh=AddPML(mesh,4);
  73.  
  74. disp(['number of cells: ' num2str(1e-6*numel(mesh.x)*numel(mesh.y)*numel(mesh.z)) 'Mcells'])
  75.  
  76. CSX=DefineRectGrid(CSX,unit,mesh);
  77.  
  78. Sim_Path=['tmp_' mfilename];
  79. Sim_CSX=[mfilename '.xml'];
  80. confirm_recursive_rmdir(0);
  81. [status,message,messageid]=rmdir(Sim_Path,'s');
  82. [status,message,messageid]=mkdir(Sim_Path);
  83. WriteOpenEMS([Sim_Path '/' Sim_CSX],FDTD,CSX);
  84. if disgeomplot==0
  85.   CSXGeomPlot( [Sim_Path '/' Sim_CSX],['--export-polydata-vtk=' Sim_Path ' --RenderDiscMaterial -v']);
  86. endif
  87. RunOpenEMS(Sim_Path,Sim_CSX);
  88.  
  89. port={port1 port2};
  90.  
  91. freq=linspace(f0-fc,f0+fc,10001);
  92. port_calc=calcPort(port,Sim_Path,freq,'RefImpedance',50);
  93.  
  94. s11=port_calc{1}.uf.ref./port_calc{1}.uf.inc;
  95. s21=port_calc{2}.uf.ref./port_calc{1}.uf.inc;
  96. s12=port_calc{1}.uf.ref./port_calc{2}.uf.inc;
  97. s22=port_calc{2}.uf.ref./port_calc{2}.uf.inc;
  98.  
  99. ##[fid,msg]=fopen('out.s2p','w');
  100. ##wdata=[freq' real(s11)' imag(s11)' real(s21)' imag(s21)' real(s12)' imag(s12)' real(s22)' imag(s22)'];
  101. ##dlmwrite(fid,wdata,'delimiter','\t');
  102. ##fclose(fid);
  103.  
  104. endfunction
磁性体シートに厚みをもたせたせいでシミュレーション時間がめっちゃ伸びたー;
受信側コイルの等価回路を出してみると
記号 磁性体あり 磁性体なし
L[nH] 721.038592 644.510935
Rs[ohm] -3.89987876 0.683139303
Cp[pF] -5.81069019 1.61053898
Rp[ohm] 674.748742 4262222.68
(Cpadd)[pF] 196 213
カーブフィットの結果はめっちゃ一致しているんだけど、値が変なんよねー、、、まぁしょうがない。あまり考えない。ちなみに送信側はほとんど影響を受けてないってことにする。めんどくさいから。
んで、結合係数を出してみる。
コイル間距離[mm] 磁性体あり 磁性体なし
2 0.1708 0.1616
4 0.1534 0.1448
12 0.0819 0.0775
20 0.0426 0.0403
うおー結合係数が上がっているーって言いたいけど、等価回路計算結果とOpenEMS結果の一致度がイマイチなんよねー、、、
ってのと、結合係数で比較したらざっくり1mmちょっとの改善くらいかなー;

2025年8月21日木曜日

NFCアンテナ(8)というかOpenEMS

OpenEMSでメタルを完全導体から銅に変更して、シミュレーションと実測の結果を比較していく。


OpenEMSのLegacy WikiのOnline ManualのPropertiesのMetalによると、AddMetalに代えてAddConductingSheetとすることで、電気伝導率を設定したメタルにすることができるらしい。しかも、メタル厚さはここで指定するので、形状データに厚さを入れ込む必要はなさそう。
んで、
gen_small_loop_coil.mのなかの
CSX=AddMetal(CSX,metal_name);

#CSX=AddMetal(CSX,metal_name);
CSX=AddConductingSheet(CSX,metal_name,58e6,40e-6);
ってする。
んで、また約7時間かけて実行する。
コイル間距離=96mmで同調をとった後、コイル間距離=20mmで実測と比較
まぁまぁ合ってると言っていいんじゃないかなー
で、もちっと詳細にコイルの等価回路を出していこうと思う。シミュレーションデータはたぶん特に解析周波数の端付近が精度が悪かったりおかしな結果になってたりするっぽいので、波形を理解して定数を推定できない(と思う)。んで、力任せにカーブフィットで定数を推定していく。解析の中心周波数付近で、インピーダンスについて等価回路の計算結果とOpenEMSの結果が一致するようにする。
こんなコードで
coil_param_fit.py
  1. import numpy as np
  2. import skrf as rf
  3. import matplotlib.pyplot as plt
  4. import scipy
  5.  
  6. def Calc_Network(L,Rs,Cp,Cpadd,Rp,gnd,port1):
  7.     cnx=[
  8.         [(port1,0),(L,0),(Cp,0),(Cpadd,0),(Rp,0)],
  9.         [(L,1),(Rs,0)],
  10.         [(Rs,1),(Cp,1),(Cpadd,1),(Rp,1),(gnd,0)]
  11.     ]
  12.     cir=rf.Circuit(cnx)
  13.     ntw=cir.network
  14.     return ntw
  15.  
  16. FILE1=rf.Network('../out_cu_96.s2p')
  17. FILE1.name='FILE1'
  18. f_start=FILE1.frequency.start
  19. f_stop=FILE1.frequency.stop
  20. nop=FILE1.frequency.npoints
  21. freq=FILE1.frequency
  22.  
  23. tl_media = rf.DefinedGammaZ0(freq, z0=50)
  24. gnd = rf.Circuit.Ground(freq, name='gnd')
  25. port1 = rf.Circuit.Port(freq, name='port1', z0=50)
  26.  
  27. f_range_lower=12e6
  28. f_range_upper=15e6
  29.  
  30. L_def=1540e-9
  31. Rs_def=0.5
  32. Cp_def=0.66e-12
  33. Rp_def=75000
  34. Cpadd_def=89e-12
  35.  
  36. tl_media_file= rf.DefinedGammaZ0(freq, z0=50)
  37. gnd_file = rf.Circuit.Ground(freq, name='gnd_file')
  38. port1_file = rf.Circuit.Port(freq, name='port1_file', z0=50)
  39. Cpadd_file=tl_media.capacitor(Cpadd_def, name='Cpadd')
  40. cnx_file=[
  41.     [(port1_file,0),(FILE1,0),(Cpadd_file,0)],
  42.     [(gnd_file,0),(Cpadd_file,1)]
  43. ]
  44. cir_file=rf.Circuit(cnx_file)
  45. ntw_file=cir_file.network
  46.  
  47. x0=(L_def,Rs_def,Cp_def,Rp_def)
  48.  
  49. def generate_network(L_val,Rs_val,Cp_val,Rp_val):
  50.     L=tl_media.inductor(L_val,name='L')
  51.     Rs=tl_media.resistor(Rs_val,name='Rs')
  52.     Cp=tl_media.capacitor(Cp_val, name='Cp')
  53.     Rp=tl_media.resistor(Rp_val,name='Rp')
  54.     Cpadd=tl_media.capacitor(Cpadd_def, name='Cpadd')
  55.     ntw=Calc_Network(L,Rs,Cp,Cpadd,Rp,gnd,port1)
  56.     return ntw
  57.  
  58. def get_err(x):
  59.     ntw=generate_network(*x)
  60.     irange=np.where((ntw.f>=f_range_lower)&(ntw.f<=f_range_upper))
  61.     calc_z_re=ntw.z_re[irange,0,0][0]
  62.     file_z_re=ntw_file.z_re[irange,0,0][0]
  63.     calc_z_im=ntw.z_im[irange,0,0][0]
  64.     file_z_im=ntw_file.z_im[irange,0,0][0]
  65.     err_re=np.sum((file_z_re-calc_z_re) ** 2)
  66.     err_im=np.sum((file_z_im-calc_z_im) ** 2)
  67.     print(err_re+err_im)
  68.     return err_re+err_im
  69.  
  70. res=scipy.optimize.fmin(get_err,x0)
  71. print(res)
  72.  
  73. ntw=generate_network(*res)
  74. irange=np.where((ntw.f>=f_range_lower)&(ntw.f<=f_range_upper))
  75. fig=plt.figure()
  76.  
  77. ax1=fig.add_subplot(2,1,1)
  78. ax1.plot(ntw.f[irange],ntw.z_re[irange,0,0][0])
  79. ax1.plot(ntw_file.f[irange],ntw_file.z_re[irange,0,0][0])
  80.  
  81. ax2=fig.add_subplot(2,1,2)
  82. ax2.plot(ntw.f[irange],ntw.z_im[irange,0,0][0])
  83. ax2.plot(ntw_file.f[irange],ntw_file.z_im[irange,0,0][0])
  84.  
  85. fig.tight_layout()
  86. plt.show()
ついでに、手作りコイルも実測データからこのコードで等価回路定数を推定してしまう。
そすっと
OpenEMS 手作りコイル
TX RX TX RX
L[nH] 1506.69974 644.510935 1535.74693 677.826358
Rs[ohm] 0.692581008 0.683139303 3.82118882 1.63220125
Cp[pF] 2.64885043 1.61053898 13.6028450 11.4099153
Rp[ohm] 3239658.97 4262222.68 22713.4181 3201.28163
(Cpadd)[pF] 89 213 76 191
ってことで、インダクタンス値についてはOpenEMSと手作りコイルでまぁまぁいいレベルで一致しているみたい。Cはまぁどうでもいいとして(手作りコイルは測定のためにツイストで引き出していてそれゆえ容量多くなっているから)、Rはやっぱりあわんのやねーしゃーない。ちなみに、推定した等価回路とOpenEMSやら手作りコイルの実測やらと比較すると、ほんと見た目では差がないくらい一致していた。scipy.optimizeえらい!
で、この定数を使った等価回路と、OpenEMSまたは手作り実測とを比較することで結合係数を推定してみる。
まぁ、こんな感じで、
コイルの結合係数kを調整していく。
これもscikit-rfとscipy.optimizeでやるって手もあるんだけど、1パラメータなので、手でいいかなーって;
コイル間距離[mm] OpenEMS 手作りコイル
2 0.1616 0.1627
4 0.1448 0.1455
12 0.0775 0.0759
20 0.0403 0.0411
なんともOpenEMSと実測はほぼ一致している!やるなーOpenEMS!日曜技術者の器用さもハンパねーって自画自賛(≧▽≦)
賑やかしのため、等価回路と実測を比較したものも載せておく
んで、これでどうしようかって言うと、、、とにかく通信距離を伸ばす方法を考えたいんだけど、まだまだ調査が必要な予感;

2025年8月16日土曜日

NFCアンテナ(7)

 

ループコイルアンテナの等価回路定数を算出して考察してみる。


インダクタの等価回路はいくつか考案されているけど、まぁこれでやってみる。
で、どういう特性をもってこれらの素子の値を推定するかというと、ここ(https://sunday-engineer.jimdofree.com/2022/10/09/%E3%82%B3%E3%82%A4%E3%83%AB-1/)に書いてある。が、再度書くと、
1)Lは自己共振周波数より十分低い周波数でX/omegaとして推定。
2)CpはLと並列共振を作るので、共振周波数から推定。
3)Rpは共振周波数でのインピーダンスの上限を与えるもので、インピーダンスの上限(=共振時のインピーダンス)から推定。
4)Rsは線材による抵抗なので、低周波でのレジスタンスから推定。
今回、自己共振周波数までデータを採ってないから、2)3)については、並列にキャパシタを追加して共振周波数を下げた状態で推定する。
TXアンテナについて
んで、
L=1544nH
Cp=13.2pF(89.2-76)
Rs=1.46ohm
Rp=3741ohm
(76pF追加したときに共振周波数13.56MHzであり、Lは1544nHであることから算出) となる。
ちなみにシミュレーターをQucsatorにしないと、数式が面倒だったり、1ポートでなぜか計算してくれなかったりする。
RXアンテナも同様にして
L=678nH
Cp=12.2pF(203.2-191)
Rs=0.689ohm
Rp=1239ohm
となる。
で、こんな回路を組んで、、、ちなみにコイルの極性と結合係数を何となくいじって、
こうなる
実測データはこうなので、
まぁまぁ再現できている。
こっからどういじっていくかを考えないといけないけど、、、なんかこっからがしんどい

NFCアンテナ(6)というかOpenEMS

 

NFCアンテナのシミュレーションをしてみたくなったのでOpenEMSでやってみた。


NFCで主流のISO14443Aってproximity card(近接カード)とかcontactless card(非接触カード)であって、タッチすることが前提の規格だと思うんだけど、なぜか通信距離を求められることが多い。だったらvicinity card(近傍カード=ISO15693)にすればいいのにってのは許されない。いろんなしがらみがあるんだと思う。ほんと大人の世界はいやらしい。は、さておき、こういうのなかなか実験できない場合にシミュレーションで何とかしたくなることもある(実験の方が早いことも多々あるので決断は慎重に)。
では、OpenEMSでシミュレーションしてみよう。OpenEMSの環境構築はまぁあっちこっち検索すればわかるっしょ。Octaveをインストールして、、、云々。
例によって唐突にソースコードはこちら。

gen_small_loop_coil.m
  1. function [CSX port]=gen_small_loop_coil(
  2.   CSX,
  3.   loop_name,
  4.   coil_width,coil_height,coil_nturn,
  5.   metal_width,metal_pitch,metal_thickness,
  6.   sub_thickness,
  7.   series_cap_value,parallel_cap_value,port_n,port_excite,
  8.   x_origin,y_origin,z_origin
  9. )
  10.  
  11. metal_name=[loop_name '_metal'];
  12. CSX=AddMetal(CSX,metal_name);
  13. parallel_cap_name=[loop_name '_pcap_z'];
  14. CSX=AddLumpedElement(CSX,parallel_cap_name,'z','C',parallel_cap_value);
  15. series_cap_name=[loop_name '_scap_z'];
  16. CSX=AddLumpedElement(CSX,series_cap_name,'z','C',series_cap_value);
  17. x_start=coil_width/2-metal_width/2;
  18. x_end=x_start-(coil_nturn)*metal_pitch;
  19. x=x_start:-metal_pitch:x_end;
  20. y_start=coil_height/2-metal_width/2;
  21. y_end=y_start-(coil_nturn)*metal_pitch;
  22. y=y_start:-metal_pitch:y_end;
  23.  
  24. z0=-sub_thickness/2;
  25. z1=sub_thickness/2;
  26.  
  27. start_stop_vector=[metal_width/2 metal_width/2 metal_thickness/2];
  28. origins=[x_origin y_origin z_origin];
  29.  
  30. for i=1:coil_nturn
  31.   if i>1
  32.     point1=[-x(i-1) -y(i) z1]+origins-start_stop_vector;
  33.     point2=[x(i) -y(i) z1]+origins+start_stop_vector;
  34.   else
  35.     point1=[-x(coil_nturn) -y(i) z1]+origins-start_stop_vector;
  36.     point2=[x(i) -y(i) z1]+origins+start_stop_vector;
  37.   endif
  38.   CSX=AddBox(CSX,metal_name,1,point1,point2);
  39.  
  40.   point1=[x(i) -y(i) z1]+origins-start_stop_vector;
  41.   point2=[x(i) y(i) z1]+origins+start_stop_vector;
  42.   CSX=AddBox(CSX,metal_name,1,point1,point2);
  43.  
  44.   point1=[-x(i) y(i) z1]+origins-start_stop_vector;
  45.   point2=[x(i) y(i) z1]+origins+start_stop_vector;
  46.   CSX=AddBox(CSX,metal_name,1,point1,point2);
  47.  
  48.   point1=[-x(i) -y(i+1) z1]+origins-start_stop_vector;
  49.   point2=[-x(i) y(i) z1]+origins+start_stop_vector;
  50.   CSX=AddBox(CSX,metal_name,1,point1,point2);
  51. endfor
  52.  
  53. point1=[-x(coil_nturn) -y(1) z0]+origins-start_stop_vector;
  54. point2=[-x(coil_nturn) -y(coil_nturn+1) z0]+origins+start_stop_vector;
  55. CSX=AddBox(CSX,metal_name,1,point1,point2);
  56.  
  57. point1=[-x(coil_nturn) -y(coil_nturn+1) z0]+origins-start_stop_vector;
  58. point2=[-x(coil_nturn) -y(coil_nturn+1) z1]+origins+start_stop_vector;
  59. CSX=AddBox(CSX,series_cap_name,10,point1,point2);
  60.  
  61. point1=[-x(coil_nturn) -y(1) z0]+origins+start_stop_vector.*[-1 1 1];
  62. point2=[-x(coil_nturn) -y(1) z1]+origins+start_stop_vector.*[1 1 -1];
  63. CSX=AddBox(CSX,parallel_cap_name,10,point1,point2);
  64.  
  65. point1=[-x(coil_nturn) -y(1) z0]+origins+start_stop_vector.*[-1 -1 1];
  66. point2=[-x(coil_nturn) -y(1) z1]+origins+start_stop_vector.*[1 -1 -1];
  67. [CSX port]=AddLumpedPort(CSX,100,port_n,50,point1,point2,[0 0 1],port_excite);
  68.  
  69.  
  70. endfunction

nfc_trx_analysis_main.m
  1. function [freq s11 s21 s12 s22]=nfc_trx_analysis_main(
  2.   port_direction,
  3.   tx_coil_width,tx_coil_height,tx_coil_nturn,
  4.   rx_coil_width,rx_coil_height,rx_coil_nturn,
  5.   trx_distance,
  6.   disgeomplot
  7. )
  8.  
  9. physical_constants;
  10. unit=1e-3;
  11.  
  12. FDTD=InitFDTD('NrTS',4e6,'EndCriteria',1e-3);
  13. f0=14e6;
  14. fc=12e6;
  15. FDTD=SetGaussExcite(FDTD,f0,fc);
  16. BC={'MUR','MUR','MUR','MUR','MUR','MUR'};
  17. FDTD=SetBoundaryCond(FDTD,BC);
  18.  
  19. CSX=InitCSX();
  20. if port_direction>0
  21.   txport_excite=false;
  22.   rxport_excite=true;
  23. else
  24.   txport_excite=true;
  25.   rxport_excite=false;
  26. endif
  27. [CSX port1]=gen_small_loop_coil(
  28.   CSX,
  29.   'tx',
  30.   tx_coil_width,tx_coil_height,tx_coil_nturn,
  31.   0.2,0.4,0,
  32.   0.2,
  33.   1,1e-18,1,txport_excite,
  34.   0,0,trx_distance/2
  35. );
  36. [CSX port2]=gen_small_loop_coil(
  37.   CSX,
  38.   'rx',
  39.   rx_coil_width,rx_coil_height,rx_coil_nturn,
  40.   0.2,0.4,0,
  41.   0.2,
  42.   1,1e-18,2,rxport_excite,
  43.   0,0,-trx_distance/2
  44. );
  45.  
  46. ## magnetic material
  47. ##CSX = AddMaterial(CSX, 'mag_material');
  48. ##CSX = SetMaterialProperty(CSX, 'mag_material', 'Mue',45);
  49. ##x_mag=rx_coil_width/2-(rx_coil_nturn-1)*0.4-0.2-1;
  50. ##y_mag=rx_coil_height/2-(rx_coil_nturn-1)*0.4-0.2-1;
  51. ##z_mag=0.2/2;
  52. ##mag_origins=[0 0 -trx_distance/2];
  53. ##CSX=AddBox(CSX,'mag_material',1,[-x_mag -y_mag z_mag]+mag_origins,[x_mag y_mag z_mag]+mag_origins);
  54. ##
  55.  
  56. air_box=16;
  57. mesh_box_size=[20 20 16];
  58. mesh_box_resolution=16;
  59. mesh=DetectEdges(CSX);
  60. mesh.x=[-mesh_box_size(1) mesh.x +mesh_box_size(1)];
  61. mesh.y=[-mesh_box_size(2) mesh.y +mesh_box_size(2)];
  62. mesh.z=[-mesh_box_size(3) mesh.z +mesh_box_size(3)];
  63.  
  64. mesh=SmoothMesh(mesh,mesh_box_resolution);
  65.  
  66. mesh.x=[-air_box+mesh.x(1) mesh.x mesh.x(end)+air_box];
  67. mesh.y=[-air_box+mesh.y(1) mesh.y mesh.y(end)+air_box];
  68. mesh.z=[-air_box+mesh.z(1) mesh.z mesh.z(end)+air_box];
  69.  
  70. mesh=SmoothMesh(mesh,c0/(f0+fc)/unit/10,1.5,'algorithm',1);
  71.  
  72. mesh=AddPML(mesh,4);
  73.  
  74. disp(['number of cells: ' num2str(1e-6*numel(mesh.x)*numel(mesh.y)*numel(mesh.z)) 'Mcells'])
  75.  
  76. CSX=DefineRectGrid(CSX,unit,mesh);
  77.  
  78. Sim_Path=['tmp_' mfilename];
  79. Sim_CSX=[mfilename '.xml'];
  80. confirm_recursive_rmdir(0);
  81. [status,message,messageid]=rmdir(Sim_Path,'s');
  82. [status,message,messageid]=mkdir(Sim_Path);
  83. WriteOpenEMS([Sim_Path '/' Sim_CSX],FDTD,CSX);
  84. if disgeomplot==0
  85.   CSXGeomPlot( [Sim_Path '/' Sim_CSX],['--export-polydata-vtk=' Sim_Path ' --RenderDiscMaterial -v']);
  86. endif
  87. RunOpenEMS(Sim_Path,Sim_CSX);
  88.  
  89. port={port1 port2};
  90.  
  91. freq=linspace(f0-fc,f0+fc,10001);
  92. port_calc=calcPort(port,Sim_Path,freq,'RefImpedance',50);
  93.  
  94. s11=port_calc{1}.uf.ref./port_calc{1}.uf.inc;
  95. s21=port_calc{2}.uf.ref./port_calc{1}.uf.inc;
  96. s12=port_calc{1}.uf.ref./port_calc{2}.uf.inc;
  97. s22=port_calc{2}.uf.ref./port_calc{2}.uf.inc;
  98.  
  99. ##[fid,msg]=fopen('out.s2p','w');
  100. ##wdata=[freq' real(s11)' imag(s11)' real(s21)' imag(s21)' real(s12)' imag(s12)' real(s22)' imag(s22)'];
  101. ##dlmwrite(fid,wdata,'delimiter','\t');
  102. ##fclose(fid);
  103.  
  104. endfunction

nfc_trx_analysis.m
  1. close all
  2. clear all
  3. clc
  4.  
  5. distance=[96 20 16 12 8 4 2 1];
  6.  
  7. for i=1:length(distance)
  8.   [freq_1 s11_1 s21_1 s12_1 s22_1]=nfc_trx_analysis_main(0,36,32,4,26,12,4,distance(i),1);
  9.   [freq_2 s11_2 s21_2 s12_2 s22_2]=nfc_trx_analysis_main(1,36,32,4,26,12,4,distance(i),1);
  10.  
  11.   fn=['out_' num2str(distance(i)) '.s2p']
  12.   #fn=['out_m45_' num2str(distance(i)) '.s2p']
  13.   [fid,msg]=fopen(fn,'w');
  14.   fprintf(fid,"# Hz S RI R 50\n");
  15.   wdata=[freq_1' real(s11_1)' imag(s11_1)' real(s21_1)' imag(s21_1)' real(s12_2)' imag(s12_2)' real(s22_2)' imag(s22_2)'];
  16.   dlmwrite(fid,wdata,'delimiter','\t');
  17.   fclose(fid);
  18. endfor

コードの構成がまぁまぁいびつなんだけど、しゃーない。OpenEMSで集中定数も含めてシミュレーションできるので、そのようなコードにしているけど、今回は無効になるような値を入れて無効化している。OpenEMSで2portのシミュレーションをするには、port1をexcite=trueにしたケースとport2をexcite=trueにしたケースの2回のシミュレーションが必要になる。んで、コイル間距離を8パタンやってしまおうという上記のコードを我が家のCorei5 Ubuntu24.02マシンで実行すると6.5時間くらいで完了。まぁ思ったより短い。てか短くするためにいろいろごまかしている。メタルはperfect electric conductor(PEC)で、厚さを0としている。誘電体は入れてないので真空にメタルのコイルが浮かんでるモデル。
で、結果を見てみる。
コイル間距離96mm(まぁ十分遠いのでほぼ結合していないと見ていい状態)のSパラファイルをQucs-sで見てみて、で、同調も取ってみる。
ってなるので、TXコイルは1548nH、RXコイルは686nHってことになる。あってるかどうか、、、例のサイトによると、TXコイルは1530nH、RXコイルは670nHってなるっぽい。まぁまぁあってるすごい。ちなみに、同調とってインダクタンスを推定するのはシミュレーションデータだからこそたぶんやっても大丈夫なやつで、実測データには寄生容量が含まれているので、このやり方ではなく、過去、日曜技術者がやったように低周波で \[ L=\frac{X}{\omega} \] をインダクタンス値とすべきである(はず)。

で、実際のものはどんな感じなのかって思う、、、完全に同じものは無理だけど、こんな感じで大体で作っちゃう。
んで、
TXアンテナは
ってことで1540nH。
RXアンテナは
ってことで690nH。
すごいね、よくあってる。むしろ工作が正確なのにびっくり。
では、2つのコイルを結合させてみる。とりあえず、20mm。
こんなかんじ。スペーサーボルトを駆使してちょうどコイル間が20mmになっている。
これを我が家のNanoVNA H4で測定するにはport1とport2を入れ替えて2回測定する必要がある。、、、やっぱまともなネットアナが欲しいなって思っちゃうけど、がまんも大事。
んで、見てみるわけだが、まずはOpenEMSのシミュレーション結果がこちら(注:Qucs-s回路図内の同調Cは無効化している)。
んで、実測結果はこちら。
いや、これがまた、よくあってるんよ。OpenEMSがまぁまぁ使えるってのと日曜技術者の器用さがまだまだ損なわれていないことに超びっくり。

んで、実測データに対してQucs-sで狙いの共振周波数を13.56MHzとして同調をとってみる。
送信側は76pF、受信側は191pFでそれぞれ直列共振周波数が13.56MHz、並列共振周波数が13.56MHzあたりになる。
で、この状態でACシミュレーションをしてみる。
これって入力している電圧源が1Vrmsに対して、出力が7.5Vrmsでるってことだと思うんだけど、ほんとかな?と思いながら今回をクローズする。これの実測はやらんといかんのはわかるけど、やる気が出るかどうか、、、

いやーさぼりにさぼった、、、このブログを購読している人はレアだろうから、まいっか。