openEMSで2.4GHzのバンドパスフィルタ

今日は友達にopenEMSの使い方を教えるために、簡単なバンドパスフィルタをシミュレーションした。 最初にQucsを使って適当にマイクロストリップのフィルタを作ってみた。基板はFR4で誘電率4.4、厚さ1.6 mmとした。マイクロストリップの幅は3 mmでだいたい50 Ωになる。長さ34 mmで2.4 GHz付近で共振するはず。λ/4だけカップリングさせて、はじっこは折り曲げたあと長さ10 mmのフィードラインを通してポートに接続した。

Qucsで簡単なフィルタを設計

Qucsのシミュレーション結果は下のようになった。センターは2.4 GHzよりちょっと低い2.34 GHzくらいになった。これをあとでopenEMSのシミュレーション結果と比較する。

Qucsのシミュレーション結果

久々のopenEMSだったが、なんとかなった。記事の一番下にOctaveスクリプトを載せる。openEMSでは3DモデルをOctave/Matlabスクリプトで描く必要があり、最初は結構これに苦労するのだが、その分シミュレーションが動いたときの達成感も大きい。メッシュは金属のエッジ付近を細かく、何もないところは粗くするようにした。最初にDetectEdges関数でメタルのエッジを検出したあと、resolution/5だけずらしたものを加えて、さらにDetectEdges関数で全体のエッジを検出し、SmoothMesh関数で滑らかにメッシュを分割した。

openEMSのモデル

openEMSのシミュレーション結果は下のようになった。センターは2.31 GHzくらいでQucsの結果より若干低い。S21の落ち方がQucsよりゆるいかんじ。

openEMSのシミュレーション結果

フィールドダンプを加えて電界分布をParaViewで表示させてみた。いいね。

電界分布をParaViewで表示

Octaveスクリプト

close all
clear
clc

physical_constants;
unit = 1e-3;

% Design parameters
W = 3;   % MSL width
L = 34;  % MSL length
S = 1;   % MSL spacing
t = 1.6; % FR4 substrate thickness
H = 10;  % Simulation box height
Lf = 10; % Feeding line length
Ws = 50; % Substrate width
Ls = 90; % Substrate length

epsilon_r = 4.4; % FR4 dielectric constant
f0 = 2.4e9; % Center frequency
fc = 1e9;   % Simulation frequency range

% Setup FDTD
FDTD = InitFDTD('EndCriteria', 1e-5, 'NrTS', 50000);
%FDTD = InitFDTD('EndCriteria', 1e-5, 'NrTS', 20000, 'OverSampling', 50);
FDTD = SetGaussExcite(FDTD, f0, fc);
BC = { 'MUR' 'MUR' 'MUR' 'MUR' 'PEC' 'MUR' };
FDTD = SetBoundaryCond(FDTD, BC);

% Setup CSX
CSX = InitCSX();

% Materials
% FR4
CSX = AddMaterial(CSX, 'FR4');
CSX = SetMaterialProperty(CSX, 'FR4', 'Epsilon', epsilon_r);
% Metal
CSX = AddMetal(CSX, 'PEC');

% Substrate
start = [-Ws/2 -Ls/2 0];
stop  = [Ws/2 Ls/2 t];
CSX = AddBox(CSX, 'FR4', 1, start, stop);

% MSL
start = [-W/2 -L/2 t];
stop  = [W/2 L/2 t];
CSX = AddBox(CSX, 'PEC', 999, start, stop);

start = [W/2+S 0 t];
stop  = [1.5*W+S L t];
CSX = AddBox(CSX, 'PEC', 999, start, stop);

start = [-W/2-S 0 t];
stop  = [-1.5*W-S -L t];
CSX = AddBox(CSX, 'PEC', 999, start, stop);

start = [1.5*W+S L t];
stop  = [1.5*W+S+Lf L-W t];
CSX = AddBox(CSX, 'PEC', 999, start, stop);

start = [-1.5*W-S -L t];
stop  = [-1.5*W-S-Lf -L+W t];
CSX = AddBox(CSX, 'PEC', 999, start, stop);

% Lumped ports
% Port 1
start = [-1.5*W-S-Lf -L+W t];
stop  = [-1.5*W-S-Lf -L 0];
[CSX port{1}] = AddLumpedPort(CSX, 999, 1, 50, start, stop, [0 0 1], true);
% Port 2
start = [1.5*W+S+Lf L-W t];
stop  = [1.5*W+S+Lf L 0];
[CSX port{2}] = AddLumpedPort(CSX, 999, 2, 50, start, stop, [0 0 1], false);

% Field dump
start = [-Ws/2 -Ls/2 t/2];
stop  = [Ws/2 Ls/2 t/2];
CSX = AddDump(CSX, 'Et');
CSX = AddBox(CSX, 'Et', 0, start, stop);

% Mesh
mesh.x = [];
mesh.y = [];
mesh.z = [];

mesh = DetectEdges(CSX, mesh, 'SetPropertyType', {'Metal', 'Excitation'});
resolution = 1;

mesh.x = [mesh.x-resolution/5 mesh.x mesh.x+resolution/5];
mesh.y = [mesh.y-resolution/5 mesh.y mesh.y+resolution/5];
mesh.z = [mesh.z resolution/5 t-resolution/5 t+resolution/5 H];

mesh = DetectEdges(CSX, mesh);
mesh = SmoothMesh(mesh, resolution);

CSX = DefineRectGrid(CSX, unit, mesh);

% Write openEMS compatible xml-file
Sim_Path = 'tmp';
Sim_CSX = 'BandpassFilter2400MHz.xml';
[status message messageid] = rmdir(Sim_Path, 's');
[status message messageid] = mkdir(Sim_Path);

WriteOpenEMS([Sim_Path '/' Sim_CSX], FDTD, CSX);
CSXGeomPlot([Sim_Path '/' Sim_CSX]);

RunOpenEMS(Sim_Path, Sim_CSX, '');

% Post processing
f = linspace(f0-fc, f0+fc, 401);
port = calcPort(port, Sim_Path, f, 'RefImpedance', 50);

S11 = port{1}.uf.ref ./ port{1}.uf.inc;
S21 = port{2}.uf.ref ./ port{1}.uf.inc;

figure('Position', [100 100 480 320]);
plot(f/1e9, 20*log10(abs(S11)), 'r-', 'DisplayName', 'S_{11}');
hold on
plot(f/1e9, 20*log10(abs(S21)), 'b-', 'DisplayName', 'S_{21}');
grid on
legend('Location', 'southeast');
xlabel('Frequency (GHz)');
ylabel('S Parameters (dB)');
xlim([f0-fc f0+fc]/1e9);