MATLAB GUI仿真设计——双缝干涉
本文旨在为读者提供关于MATLAB双缝干涉仿真GUI应用程序的设计思路。该应用程序通过图形用户界面(GUI)模拟经典的物理实验——双缝干涉,允许用户调整相关物理参数,观察并分析干涉现象。
一、双缝干涉原理
1. 波的性质
波是一个或多个物理量的动态扰动,它从平衡状态传播出去。波的传播不涉及质点的移动,而是振动状态的传播。数学上,任何沿一定方向传播的函数形状都可视为波。因此,波函数可表示为:
f ( x , t ) = A cos ( ω ( t − x v ) + φ ) f(x,t) = A\cos\left(\omega(t-\frac{x}{v})+\varphi\right) f(x,t)=Acos(ω(t−vx)+φ)
波动方程表示为:
∂ 2 f ∂ t 2 = c 2 ∂ 2 f ∂ x 2 \frac{\partial^2 f}{\partial t^2} = c^2 \frac{\partial^2 f}{\partial x^2} ∂t2∂2f=c2∂x2∂2f
其中,(c)表示波的传播速度。从这个方程可知,在相同的均匀介质中,波的传播速度是恒定的,且波的频率保持不变。
2. 光的干涉
当两波在同一介质中传播并发生重叠时,重叠区域内的介质质点同时受到两波的作用。如果波的振幅不大,那么该区域内的介质质点的振动位移等于单独波动引起的位移的矢量和,这称为波的叠加原理。
当两波频率相同且具有恒定的相位差,并且它们的振动方向不完全垂直时,这两波的叠加会产生新的稳定波形,即发生波的干涉。
双缝干涉是波动光学中的一个重要现象。当光波通过两个相近的狭缝时,从两个狭缝发出的光波在屏幕上相遇并发生干涉,形成明暗相间的干涉条纹。
3. 干涉光强
简单起见,假设两波表达为:
E 1 = A 1 cos ( ω t + φ 1 ) E 2 = A 2 cos ( ω t + φ 2 ) E_1 = A_1 \cos(\omega t + \varphi_1) \\ E_2 = A_2 \cos(\omega t + \varphi_2) E1=A1cos(ωt+φ1)E2=A2cos(ωt+φ2)
它们的叠加结果为:
E = E 1 + E 2 = A cos ( ω t + φ ) E = E_1 + E_2 = A\cos(\omega t + \varphi) E=E1+E2=Acos(ωt+φ)
其中,
A
2
=
A
1
2
+
A
2
2
+
2
A
1
A
2
cos
(
φ
2
−
φ
1
)
tan
φ
=
A
1
sin
φ
1
+
A
2
sin
φ
2
A
1
cos
φ
1
+
A
2
cos
φ
2
A^2 = A_1^2 + A_2^2 + 2A_1A_2\cos(\varphi_2-\varphi_1) \\ \tan \varphi = \frac{A_1\sin \varphi_1 + A_2\sin \varphi_2}{A_1\cos \varphi_1 + A_2\cos \varphi_2}
A2=A12+A22+2A1A2cos(φ2−φ1)tanφ=A1cosφ1+A2cosφ2A1sinφ1+A2sinφ2
干涉后的光强为:
I = A 2 = A 1 2 + A 2 2 + 2 A 1 A 2 cos ( φ 2 − φ 1 ) I = A^2 = A_1^2 + A_2^2 + 2A_1A_2\cos(\varphi_2-\varphi_1) I=A2=A12+A22+2A1A2cos(φ2−φ1)
二、交互界面设计
1、界面设计
交互界面如下图所示:
在左侧设置一个坐标系空间以显示干涉效果。接着,添加一个面板用于参数输入,在该面板中嵌入四个数字编辑框控件以调整干涉参数。再添加一个面板,用于控制干涉动画,其中包含两个复选框控件以选择显示的干涉线类型。此外,设置三个按钮分别用于重置、播放和展示干涉效果。最后,添加一个滑块控件以调整动画播放的速度。
2. 参数传递
在程序运行前,必须先传入参数。这一过程在应用的启动回调函数(startupFcn
)中进行,如下所示:
app.Lambda = app.EditField.Value;
app.d = app.dEditField.Value;
app.D = app.DEditField.Value;
app.x = app.xEditField.Value;
app.frame = app.Slider.Value;
这段代码初始化了应用所需的关键参数,例如波长(Lambda
)、双缝间距(d
)、屏距(D
)和屏中心距(x
),以及动画播放速度(frame
)。
为了实现参数的实时更新,为每个输入字段设置了值改变的回调函数:
% Value changed function for the Lambda (wavelength) edit field
function EditFieldValueChanged(app, event)
app.Lambda = app.EditField.Value;
end
% Value changed function for the d (slit separation) edit field
function dEditFieldValueChanged(app, event)
app.d = app.dEditField.Value;
end
% Value changed function for the D (screen distance) edit field
function DEditFieldValueChanged(app, event)
app.D = app.DEditField.Value;
end
% Value changed function for the x (distance to screen center) edit field
function xEditFieldValueChanged(app, event)
app.x = app.xEditField.Value;
end
这些函数确保了当用户在界面上更改这些值时,程序内部的对应参数将即时更新。
为了确保模拟显示的合理性,我们还需要设定绘图区域的坐标轴范围,以便能够清楚地展示双缝干涉图案:
xlim(app.UIAxes, [-5, 10]); % 设置X轴的显示范围
ylim(app.UIAxes, [-5, 5]); % 设置Y轴的显示范围
这确保了不论参数如何变化,视图都保持在一个合适的显示范围内。
三、仿真代码
1、光的动态传播
(1)双缝前的传播
在仿真模型中,光的传播需要分为两个阶段:双缝前的传播和双缝后的传播。首先,我们来看双缝前光波的动态传播过程。这部分主要涉及到波的传播动画,模拟光波从发射源到达双缝的过程。
在双缝前的传播阶段,我们假设光波以圆形波前向外传播。波峰和波谷以波速(c)向外扩散,波形保持不变。这里,我们使用MATLAB中的fimplicit
函数来绘制波前。
下面的代码段演示了如何实现双缝前的光波动态传播:
function WavePT(app,t,WavePeak,WaveTrough,peakColor,troughColor,a,b,X1,X2,Y1,Y2)
hold(app.UIAxes, 'on');
for n = t:-2:1
wavePeak = n * app.Lambda / 2;
waveTrough = (n-1) * app.Lambda / 2;
% 绘制波峰
if WavePeak
fimplicit(app.UIAxes,@(X,Y) (X-a).^2 + (Y-b).^2 - wavePeak^2, [X1,X2,Y1,Y2], 'Color', peakColor);
end
if WaveTrough
% 绘制波谷
fimplicit(app.UIAxes,@(X,Y) (X-a).^2 + (Y-b).^2 - waveTrough^2, [X1,X2,Y1,Y2], 'Color', troughColor);
end
end
end
在这个函数中,wavePeak
和waveTrough
变量分别表示波峰和波谷的半径,它们随时间t
增大而线性增加。通过fimplicit
函数在坐标范围[X1, X2, Y1, Y2]
内绘制圆形波前,其中(a, b)
是波源的位置。peakColor
和troughColor
参数用于控制波峰和波谷的颜色。
通过这段代码,我们能够模拟光波在双缝前的传播过程,直观地观察波动的传播行为。接下来的部分将继续讨论光波通过双缝后的传播过程。
(2)双缝后的传播
双缝后的传播涉及到光波通过两个狭缝后的干涉效果。当光波通过双缝时,每个狭缝都可以看作是一个新的波源,发出的波将在屏幕上相遇并产生干涉。简言之,双缝之后的传播动画,就是三个光源单独的动画的叠加。
下面的代码段演示了如何实现双缝后的光波动态传播和干涉:
function wave(app,WavePeak,WaveTrough,InterferenceFringes,flag)
hold(app.UIAxes, 'on'); % 保持当前图形,允许在上面绘制更多图形元素
% 清除上一次的波峰和波谷,以避免画面过于拥挤
cla(app.UIAxes);
plot(app.UIAxes, [0, 0], [-app.d/2, app.d/2], 'k', 'LineWidth', 2);
plot(app.UIAxes, [0, 0], [app.d/2 + 0.2, 5], 'k', 'LineWidth', 2);
plot(app.UIAxes, [0, 0], [-app.d/2 - 0.2, -5], 'k', 'LineWidth', 2);
% 如果colorState字段不存在,则初始化它
if ~isfield(app, 'colorState')
app.colorState = true; % true 表示本次绘制波峰为红色,波谷为蓝色
end
% 根据colorState的值选择颜色
if app.colorState
peakColor = 'r';
troughColor = 'b';
else
peakColor = 'b';
troughColor = 'r';
end
% 绘制波峰和波谷
if flag == 0
WavePT(app,app.t,WavePeak,WaveTrough,peakColor,troughColor,-5,0,-5,0,-5,5)
elseif flag == 1
WavePT(app,app.t,WavePeak,WaveTrough,peakColor,troughColor,-5,0,-5,0,-5,5)
WavePT(app,app.T,WavePeak,WaveTrough,peakColor,troughColor,0,app.d/2,0,app.D,-5,5)
WavePT(app,app.T,WavePeak,WaveTrough,peakColor,troughColor,0,-app.d/2,0,app.D,-5,5)
end
% 切换颜色状态
app.colorState = ~app.colorState;
drawnow;
end
在这段代码中,a
和 b
代表波源位置,app.d
为双缝间的距离。通过在不同的Y
坐标(b - app.d/2
和 b + app.d/2
)上绘制波形,模拟了从两个狭缝发出的波的传播。波峰和波谷使用不同的颜色(peakColor
和 troughColor
)和线型(实线和虚线)来区分。
通过执行这段代码,我们可以观察到光波通过双缝后在屏幕上形成的干涉图样,包括干涉条纹的形成过程,从而深入理解双缝干涉现象。接下来,我们将讨论如何在仿真中模拟相长线与相消线的干涉效果。
(3)相长线与相消线
在双缝干涉的仿真中,相长线与相消线是干涉图样中的关键特征。相长线发生在两束光波的相位差为偶数倍的( \pi )时,导致光强加强;相消线则发生在相位差为奇数倍的( \pi )时,导致光强减弱。相位差通常反映为光程差,即:光程差为半波长的偶数倍时相长,为奇数倍是相消。数学上,距离之差围成的图形被称为双曲线,那么,相长线的方程可以表示为:
y 2 k 2 λ 2 4 − x 2 d 2 4 − k 2 λ 2 4 = 1 \frac{y^2}{\frac{k^2\lambda^2}{4}} - \frac{x^2}{\frac{d^2}{4}-\frac{k^2\lambda^2}{4}} = 1 4k2λ2y2−4d2−4k2λ2x2=1
相应的,相消线的方程可以表示为:
y 2 ( 2 k + 1 ) 2 λ 2 16 − x 2 d 2 4 − ( 2 k + 1 ) 2 λ 2 16 = 1 \frac{y^2}{\frac{(2k+1)^2\lambda^2}{16}} - \frac{x^2}{\frac{d^2}{4}-\frac{(2k+1)^2\lambda^2}{16}} = 1 16(2k+1)2λ2y2−4d2−16(2k+1)2λ2x2=1
上面两个式子中的(k) 取正整数。
下面是实现相长线与相消线模拟的关键代码段:
% 定时器回调函数中处理干涉条纹的逻辑
function SimFcn(app,~,~)
% 之前的代码逻辑...
InterferenceFringes = -1;
if app.Constructive && app.Destructive
InterferenceFringes = 2;
elseif app.Constructive && ~app.Destructive
InterferenceFringes = 1;
elseif ~app.Constructive && app.Destructive
InterferenceFringes = 0;
end
% 根据干涉类型调用绘制函数
wave(app, app.WavePeak, app.WaveTrough, InterferenceFringes, app.flag);
end
% 绘制波形和干涉条纹的函数 wave
function wave(app, WavePeak, WaveTrough, InterferenceFringes, flag)
% 之前的代码逻辑...
if InterferenceFringes >= 0
app.Interference(InterferenceFringes);
end
end
% 实现干涉条纹绘制的函数 Interference
function Interference(app,flag,k)
if flag == 1
% 相长线
fplot(app.UIAxes, @(x) sqrt(k^2*app.Lambda^2/(app.d^2-k^2*app.Lambda^2)*x.^2 + k^2*app.Lambda^2/4), [0, app.D], 'k');
fplot(app.UIAxes, @(x) -sqrt(k^2*app.Lambda^2/(app.d^2-k^2*app.Lambda^2)*x.^2 + k^2*app.Lambda^2/4), [0, app.D], 'k');
end
if flag == 2
% 相长线
fplot(app.UIAxes, @(x) sqrt(k^2*app.Lambda^2/(app.d^2-k^2*app.Lambda^2)*x.^2 + k^2*app.Lambda^2/4), [0, app.D], 'k');
fplot(app.UIAxes, @(x) -sqrt(k^2*app.Lambda^2/(app.d^2-k^2*app.Lambda^2)*x.^2 + k^2*app.Lambda^2/4), [0, app.D], 'k');
% 相消线
fplot(app.UIAxes, @(x) sqrt((2*k+1)^2*app.Lambda^2/(4*app.d^2-(2*k+1)^2*app.Lambda^2)*x.^2 + (2*k+1)^2*app.Lambda^2/16), [0, app.D], 'c');
fplot(app.UIAxes, @(x) -sqrt((2*k+1)^2*app.Lambda^2/(4*app.d^2-(2*k+1)^2*app.Lambda^2)*x.^2 + (2*k+1)^2*app.Lambda^2/16), [0, app.D], 'c');
end
if flag == 0
% 相消线
fplot(app.UIAxes, @(x) sqrt((2*k+1)^2*app.Lambda^2/(4*app.d^2-(2*k+1)^2*app.Lambda^2)*x.^2 + (2*k+1)^2*app.Lambda^2/16), [0, app.D], 'c');
fplot(app.UIAxes, @(x) -sqrt((2*k+1)^2*app.Lambda^2/(4*app.d^2-(2*k+1)^2*app.Lambda^2)*x.^2 + (2*k+1)^2*app.Lambda^2/16), [0, app.D], 'c');
end
end
在这段代码中,SimFcn
函数通过检查app.Constructive
和app.Destructive
变量来确定需要绘制的干涉类型,然后调用wave
函数来更新干涉图样。wave
函数根据干涉类型参数InterferenceFringes
,进一步调用Interference
函数绘制相长线和相消线。
Interference
函数中,根据type
参数来决定绘制相长线、相消线或两者兼有。具体的绘图逻辑可以通过计算干涉条纹的位置和光强分布来实现,从而在GUI中呈现出清晰的干涉图样。这种方法准确地模拟了双缝干涉实验中的物理现象,使得仿真工具能够有效地用于教学和研究目的。
(4)实现定时器控制动态传播
在仿真应用中,定时器的作用是控制光波传播的动态模拟。我们通过定时器定期更新界面上的波形,来模拟光的动态传播过程。定时器确保了在每个时间间隔内,光波的状态都能得到更新,并且在GUI上呈现出来。
定时器的实现已经嵌入在仿真代码中,具体如下:
% 定时器回调函数,用于模拟光波传播
function SimFcn(app,~,~)
% 获取CheckBoxTree_2的选中状态
checkedNodes_2 = app.Tree_2.CheckedNodes;
% 默认设置为不显示波峰和波谷
app.WavePeak = false;
app.WaveTrough = false;
% 遍历选中的节点,决定是否显示波峰和波谷
for i = 1:length(checkedNodes_2)
if isequal(checkedNodes_2(i).Text, '波峰:红色')
app.WavePeak = true;
elseif isequal(checkedNodes_2(i).Text, '波谷:蓝色')
app.WaveTrough = true;
end
end
% 获取CheckBoxTree的选中状态
checkedNodes = app.Tree.CheckedNodes;
% 默认设置为不显示相长线与相消线
app.Constructive = false;
app.Destructive = false;
% 遍历选中的节点,决定是否显示波峰和波谷
for i = 1:length(checkedNodes)
if isequal(checkedNodes(i).Text, '相长线')
app.Constructive = true;
elseif isequal(checkedNodes(i).Text, '相消线')
app.Destructive = true;
end
end
InterferenceFringes = -1;
if app.Constructive && app.Destructive
InterferenceFringes = 2;
elseif app.Constructive && ~app.Destructive
InterferenceFringes = 1;
elseif ~app.Constructive && app.Destructive
InterferenceFringes = 0;
end
% 调用wave函数,传入波峰和波谷的显示状态
if app.t < app.N
wave(app,app.WavePeak, app.WaveTrough,InterferenceFringes, 0);
elseif app.t >= app.N
wave(app,app.WavePeak, app.WaveTrough,InterferenceFringes, 1);
app.T = app.T + 1;
end
app.t = app.t + 1;
end
% 用于启动和停止定时器的播放控制按钮
function Button_2Pushed(app, event)
app.Button.Enable = "on";
app.Slider.Enable = "off";
if app.Button_2.Text == "播放"
if isempty(app.Sim) || ~isvalid(app.Sim)
% 创建或重置定时器
app.Sim = timer('ExecutionMode', 'fixedRate', 'Period', app.frame, 'TimerFcn', @app.SimFcn);
end
start(app.Sim);
app.Button_2.Text = "暂停";
elseif app.Button_2.Text == "暂停"
stop(app.Sim);
app.Button_2.Text = "播放";
end
end
% 用于重置播放动画的控制按钮
function ButtonPushed(app, event)
app.Slider.Enable = "on";
stop(app.Sim);
delete(app.Sim);
cla(app.UIAxes);
app.Button_2.Text = "播放";
app.Button.Enable = "off";
app.Slider.Enable = "on";
app.t = 1;
app.T = 1;
end
在这段代码中,定时器app.Sim
在startupFcn
函数中被初始化,并设置了执行模式、周期和回调函数。回调函数SimFcn
负责根据定时器的触发更新光波传播的动态画面。播放控制按钮通过Button_2Pushed
函数来控制定时器的启动和停止,从而控制仿真的进行。
这种设计使得用户可以通过界面上的按钮来控制仿真的播放和暂停,实现了对光波传播过程的动态观察和分析。
2、干涉图像仿真
(1)绘制干涉图像
仿真中的干涉图像绘制是通过计算双缝干涉的光程差来实现的。这一过程详细考虑了从双缝到观察屏幕上各点的光程差,进而计算出相应位置的光强度,形成干涉图样。
在ference
函数中,首先定义了波长(Lambda
)、双缝间距(d
)、屏幕距离(D
)以及观察范围(xRange
)。然后,计算每一点的光程差(Phi
),并根据光程差计算出该点的光强度(B
)。这些光强度值最终被映射为图像上的亮度,形成了干涉条纹。
function ference(app)
% 获取参数,保持使用国际单位制
Lambda = app.Lambda;
d = app.d;
Z = app.D;
xRange = app.x; % 条纹的横坐标范围
% 计算光程差
Nx = 225;
xs = linspace(-xRange, xRange, Nx);
r1 = sqrt((xs - d/2).^2 + Z^2);
r2 = sqrt((xs + d/2).^2 + Z^2);
Phi = 2 * pi * (r2 - r1) / Lambda;
% 绘制图像
B = 4 * cos(Phi / 2).^2;
Br = (B / max(B)) * 255; % 计算光强度
% 创建图像数据并绘制条纹图像
figure;
subplot(2, 1, 1);
image(xs, 0, Br);
colormap('hot');
colorbar;
axis off;
% 绘制光强图
subplot(2, 1, 2);
plot(xs, Br, 'r');
xlabel('Position (m)');
ylabel('Intensity');
axis tight;
end
(2)设置颜色
色彩的设置在干涉图像的表达中至关重要,它有助于区分不同光强度的区域。通过色彩的变化,可以清晰地区分干涉条纹中的明暗区域,使得干涉图样更加易于理解和分析。
在仿真程序中,颜色的设置是通过调整色彩映射函数实现的,如使用colormap
函数来定义不同光强度的颜色表示:
function [R,G,B] = wave2color(app,lambda)
a = lambda;
if a <= 439
R = -(a-440)/(440-380);
G = 0;
B = 1;
elseif a<=489
R = 0;
G = (a-440)/(490-440);
B = 1;
elseif a<=509
R = 0;
G = 1;
B = -(a-510)/(510-490);
elseif a<=579
R = (a-510)/(580-510);
G = 1;
B = 0;
elseif a<=644
R = 1;
G = -(a-645)/(645-580);
B = 0;
elseif a<=780
R = 1;
G = 0;
B = 0;
else
R = 1;
G = 1;
B = 1;
end
end
通过这种方式,干涉图像中的每一点都可以根据其光强度或波长映射到相应的颜色,从而实现丰富的视觉效果和直观的科学解释。
3、设置说明介绍
为了更好地理解双缝干涉仿真程序的操作和参数设置,我们可以在GUI中加入一个上下文菜单(context menu
),为用户提供详细的说明和指导。这个上下文菜单可以包含有关仿真参数、控件功能以及如何操作仿真界面的信息。
下面是设置上下文菜单并提供说明信息的代码:
function MenuSelected(app, event)
message = [
"杨氏双缝干涉仿真说明:\n", ...
"参数(单位:微米):\n", ...
" - $\lambda$: 波长\n", ...
" - $d$: 双缝间距\n", ...
" - $D$: 屏距\n", ...
" - $x$: 到屏中心距离\n\n", ...
"控件说明:\n", ...
" - 播放:开始或暂停仿真。\n", ...
" - 重置:清除仿真画面并重置参数。\n", ...
" - 仿真:根据当前参数显示干涉效果。"
];
uialert(app.UIFigure, message, "仿真说明", "Icon", 'info', 'Interpreter','latex');
end
在这段代码中,MenuSelected
函数定义了当上下文菜单被选中时展示的信息。这些信息通过uialert
函数在一个对话框中显示给用户,提供了关于仿真程序的详细说明。这种方法不仅增强了用户界面的友好性,也使得非专业用户能够更容易地理解和操作仿真程序。
四、附录
最后,附上本文的源代码:
classdef DoubleSlitInterference < matlab.apps.AppBase
% Properties that correspond to app components
properties (Access = public)
UIFigure matlab.ui.Figure
Slider matlab.ui.control.Slider
Label_2 matlab.ui.control.Label
Panel matlab.ui.container.Panel
Button_3 matlab.ui.control.Button
Button_2 matlab.ui.control.Button
Button matlab.ui.control.Button
Tree_2 matlab.ui.container.CheckBoxTree
Node_4 matlab.ui.container.TreeNode
Node_5 matlab.ui.container.TreeNode
Node_6 matlab.ui.container.TreeNode
Tree matlab.ui.container.CheckBoxTree
Node matlab.ui.container.TreeNode
Node_2 matlab.ui.container.TreeNode
Node_3 matlab.ui.container.TreeNode
Panel_2 matlab.ui.container.Panel
xEditField matlab.ui.control.NumericEditField
xEditFieldLabel matlab.ui.control.Label
DEditField matlab.ui.control.NumericEditField
DEditFieldLabel matlab.ui.control.Label
dEditField matlab.ui.control.NumericEditField
dEditFieldLabel matlab.ui.control.Label
EditField matlab.ui.control.NumericEditField
EditFieldLabel matlab.ui.control.Label
Label matlab.ui.control.Label
UIAxes matlab.ui.control.UIAxes
ContextMenu matlab.ui.container.ContextMenu
Menu matlab.ui.container.Menu
end
properties (Access = public)
Lambda % 波长
d % 双缝间距
D % 屏距
x % P到屏中心的距离
frame % 动画速度
Sim % 动画计时器
t % 运行时间(波数)
colorState = true % 初始颜色状态,true表示红色为波峰,蓝色为波谷
WavePeak % 波峰选择
WaveTrough % 波谷选择
N % 到双缝所需波数
P % 到显示处所需波数
T % 双缝之后的运行时间(波数)
Constructive % 相长线
Destructive % 相消线
end
methods (Access = public)
function SimFcn(app,~,~)
% 获取CheckBoxTree_2的选中状态
checkedNodes_2 = app.Tree_2.CheckedNodes;
% 默认设置为不显示波峰和波谷
app.WavePeak = false;
app.WaveTrough = false;
% 遍历选中的节点,决定是否显示波峰和波谷
for i = 1:length(checkedNodes_2)
if isequal(checkedNodes_2(i).Text, '波峰:红色')
app.WavePeak = true;
elseif isequal(checkedNodes_2(i).Text, '波谷:蓝色')
app.WaveTrough = true;
end
end
% 获取CheckBoxTree的选中状态
checkedNodes = app.Tree.CheckedNodes;
% 默认设置为不显示相长线与相消线
app.Constructive = false;
app.Destructive = false;
% 遍历选中的节点,决定是否显示波峰和波谷
for i = 1:length(checkedNodes)
if isequal(checkedNodes(i).Text, '相长线')
app.Constructive = true;
elseif isequal(checkedNodes(i).Text, '相消线')
app.Destructive = true;
end
end
InterferenceFringes = -1;
if app.Constructive && app.Destructive
InterferenceFringes = 2;
elseif app.Constructive && ~app.Destructive
InterferenceFringes = 1;
elseif ~app.Constructive && app.Destructive
InterferenceFringes = 0;
end
% 调用wave函数,传入波峰和波谷的显示状态
if app.t < app.N
wave(app,app.WavePeak, app.WaveTrough,InterferenceFringes, 0);
elseif app.t >= app.N
wave(app,app.WavePeak, app.WaveTrough,InterferenceFringes, 1);
app.T = app.T + 1;
end
app.t = app.t + 1;
end
function wave(app,WavePeak,WaveTrough,InterferenceFringes,flag)
hold(app.UIAxes, 'on'); % 保持当前图形,允许在上面绘制更多图形元素
% 清除上一次的波峰和波谷,以避免画面过于拥挤
cla(app.UIAxes);
plot(app.UIAxes, [0, 0], [-app.d/2, app.d/2], 'k', 'LineWidth', 2);
plot(app.UIAxes, [0, 0], [app.d/2 + 0.2, 5], 'k', 'LineWidth', 2);
plot(app.UIAxes, [0, 0], [-app.d/2 - 0.2, -5], 'k', 'LineWidth', 2);
% 如果colorState字段不存在,则初始化它
if ~isfield(app, 'colorState')
app.colorState = true; % true 表示本次绘制波峰为红色,波谷为蓝色
end
% 根据colorState的值选择颜色
if app.colorState
peakColor = 'r';
troughColor = 'b';
else
peakColor = 'b';
troughColor = 'r';
end
% 绘制波峰和波谷
if flag == 0
WavePT(app,app.t,WavePeak,WaveTrough,peakColor,troughColor,-5,0,-5,0,-5,5)
elseif flag == 1
WavePT(app,app.t,WavePeak,WaveTrough,peakColor,troughColor,-5,0,-5,0,-5,5)
WavePT(app,app.T,WavePeak,WaveTrough,peakColor,troughColor,0,app.d/2,0,app.D,-5,5)
WavePT(app,app.T,WavePeak,WaveTrough,peakColor,troughColor,0,-app.d/2,0,app.D,-5,5)
if InterferenceFringes == 0
app.Interference(0,0);
app.Interference(0,1);
app.Interference(0,2);
app.Interference(0,3);
app.Interference(0,4);
elseif InterferenceFringes == 1
app.Interference(1,0);
app.Interference(1,1);
app.Interference(1,2);
app.Interference(1,3);
app.Interference(1,4);
elseif InterferenceFringes == 2
app.Interference(2,0);
app.Interference(2,1);
app.Interference(2,2);
app.Interference(2,3);
app.Interference(2,4);
end
end
% 切换颜色状态
app.colorState = ~app.colorState;
drawnow;
end
function WavePT(app,t,WavePeak,WaveTrough,peakColor,troughColor,a,b,X1,X2,Y1,Y2)
hold(app.UIAxes, 'on');
for n = t:-2:1
wavePeak = n * app.Lambda / 2;
waveTrough = (n-1) * app.Lambda / 2;
% 绘制波峰
if WavePeak
fimplicit(app.UIAxes,@(X,Y) (X-a).^2 + (Y-b).^2 - wavePeak^2, [X1,X2,Y1,Y2], 'Color', peakColor);
end
if WaveTrough
% 绘制波谷
fimplicit(app.UIAxes,@(X,Y) (X-a).^2 + (Y-b).^2 - waveTrough^2, [X1,X2,Y1,Y2], 'Color', troughColor);
end
end
end
function Interference(app,flag,k)
if flag == 1
% 相长线
fplot(app.UIAxes, @(x) sqrt(k^2*app.Lambda^2/(app.d^2-k^2*app.Lambda^2)*x.^2 + k^2*app.Lambda^2/4), [0, app.D], 'k');
fplot(app.UIAxes, @(x) -sqrt(k^2*app.Lambda^2/(app.d^2-k^2*app.Lambda^2)*x.^2 + k^2*app.Lambda^2/4), [0, app.D], 'k');
end
if flag == 2
% 相长线
fplot(app.UIAxes, @(x) sqrt(k^2*app.Lambda^2/(app.d^2-k^2*app.Lambda^2)*x.^2 + k^2*app.Lambda^2/4), [0, app.D], 'k');
fplot(app.UIAxes, @(x) -sqrt(k^2*app.Lambda^2/(app.d^2-k^2*app.Lambda^2)*x.^2 + k^2*app.Lambda^2/4), [0, app.D], 'k');
% 相消线
fplot(app.UIAxes, @(x) sqrt((2*k+1)^2*app.Lambda^2/(4*app.d^2-(2*k+1)^2*app.Lambda^2)*x.^2 + (2*k+1)^2*app.Lambda^2/16), [0, app.D], 'c');
fplot(app.UIAxes, @(x) -sqrt((2*k+1)^2*app.Lambda^2/(4*app.d^2-(2*k+1)^2*app.Lambda^2)*x.^2 + (2*k+1)^2*app.Lambda^2/16), [0, app.D], 'c');
end
if flag == 0
% 相消线
fplot(app.UIAxes, @(x) sqrt((2*k+1)^2*app.Lambda^2/(4*app.d^2-(2*k+1)^2*app.Lambda^2)*x.^2 + (2*k+1)^2*app.Lambda^2/16), [0, app.D], 'c');
fplot(app.UIAxes, @(x) -sqrt((2*k+1)^2*app.Lambda^2/(4*app.d^2-(2*k+1)^2*app.Lambda^2)*x.^2 + (2*k+1)^2*app.Lambda^2/16), [0, app.D], 'c');
end
end
function ference(app)
% 获取参数,保持使用国际单位制
Lambda = app.Lambda;
d = app.d;
Z = app.D;
xRange = app.x; % 条纹的横坐标范围 [-x, x]
% 计算光程差
Nx = 225;
xs = linspace(-xRange, xRange, Nx);
ys = xRange;
r1 = sqrt((xs - d/2).^2 + Z^2);
r2 = sqrt((xs + d/2).^2 + Z^2);
Phi = 2 * pi * (r2 - r1) / Lambda;
% 绘制图像
B = 4 * cos(Phi / 2).^2;
NCLevels = 255;
Br = (B / max(B)) * NCLevels; % 直接计算水平方向的光强度
% 创建图像数据,为形成竖直条纹,沿Y轴复制Br
% imgData = repmat(Br, [100, 1]); % Y轴复制100倍以形成竖直条纹
% 创建新窗口并绘制条纹图像
figure;
subplot(2, 1, 1);
image(xs,ys,Br);
c = linspace(0,1,64)';
[R,G,B] = app.wave2color(Lambda*1000);
colormap([c*R,c*G,c*B]);
% imagesc([-xRange, xRange], [0, 4], imgData);
% colormap([linspace(0, 1, NCLevels)', zeros(NCLevels, 1), zeros(NCLevels, 1)]); % 红色映射
axis off; % 不显示坐标轴
% 绘制光强图
subplot(2, 1, 2);
plot(xs, Br, 'r'); % 使用红色线表示光强
xlabel('Position (m)');
ylabel('Intensity');
axis tight;
end
function [R,G,B] = wave2color(app,lambda)
a = lambda;
if a <= 439
R = -(a-440)/(440-380);
G = 0;
B = 1;
elseif a<=489
R = 0;
G = (a-440)/(490-440);
B = 1;
elseif a<=509
R = 0;
G = 1;
B = -(a-510)/(510-490);
elseif a<=579
R = (a-510)/(580-510);
G = 1;
B = 0;
elseif a<=644
R = 1;
G = -(a-645)/(645-580);
B = 0;
elseif a<=780
R = 1;
G = 0;
B = 0;
else
R = 1;
G = 1;
B = 1;
end
end
end
% Callbacks that handle component events
methods (Access = private)
% Code that executes after component creation
function startupFcn(app)
app.Lambda = app.EditField.Value;
app.d = app.dEditField.Value;
app.D = app.DEditField.Value;
app.x = app.xEditField.Value;
app.frame = app.Slider.Value;
app.N = ceil(2*sqrt(25+app.d^2/4)/app.Lambda); % 到双缝所需波数
app.P = ceil(2*sqrt(app.x^2+app.d^2/4)/app.Lambda); % 到显示处所需波数
% 设置坐标轴范围
xlim(app.UIAxes, [-5, 10]);
ylim(app.UIAxes, [-5, 5]);
app.t = 1;
app.T = 1;
app.Button.Enable = "off";
end
% Button pushed function: Button_2
function Button_2Pushed(app, event)
app.Button.Enable = "on";
app.Slider.Enable = "off";
if app.Button_2.Text == "播放"
if isempty(app.Sim) || ~isvalid(app.Sim)
% 创建或重置定时器
app.Sim = timer('ExecutionMode', 'fixedRate', 'Period', app.frame, 'TimerFcn', @app.SimFcn);
end
start(app.Sim);
app.Button_2.Text = "暂停";
elseif app.Button_2.Text == "暂停"
stop(app.Sim);
app.Button_2.Text = "播放";
end
end
% Button pushed function: Button
function ButtonPushed(app, event)
app.Slider.Enable = "on";
stop(app.Sim);
delete(app.Sim);
cla(app.UIAxes);
app.Button_2.Text = "播放";
app.Button.Enable = "off";
app.Slider.Enable = "on";
app.t = 1;
app.T = 1;
end
% Button pushed function: Button_3
function Button_3Pushed(app, event)
app.ference;
end
% Value changed function: EditField
function EditFieldValueChanged(app, event)
value = app.EditField.Value;
app.Lambda = value;
end
% Value changed function: dEditField
function dEditFieldValueChanged(app, event)
value = app.dEditField.Value;
app.d = value;
end
% Value changed function: DEditField
function DEditFieldValueChanged(app, event)
value = app.DEditField.Value;
app.D = value;
end
% Value changed function: xEditField
function xEditFieldValueChanged(app, event)
value = app.xEditField.Value;
app.x = value;
end
% Menu selected function: Menu
function MenuSelected(app, event)
message = [
"杨氏双缝干涉仿真说明:", ...
"参数(单位:$\mu$m):$\lambda$ (波长), $d$ (双缝间距), $D$ (屏距), $x$ (到屏中心距离).", ...
"控件:", ...
"- 播放:开始/暂停仿真。", ...
"- 重置:清除仿真画面。", ...
"- 仿真:显示干涉效果。"
];
uialert(app.UIFigure, message, "关于", "Icon", 'info', 'Interpreter','latex');
end
end
% Component initialization
methods (Access = private)
% Create UIFigure and components
function createComponents(app)
% Create UIFigure and hide until all components are created
app.UIFigure = uifigure('Visible', 'off');
app.UIFigure.Position = [100 100 640 480];
app.UIFigure.Name = 'MATLAB App';
% Create UIAxes
app.UIAxes = uiaxes(app.UIFigure);
app.UIAxes.Position = [30 90 360 240];
% Create Label
app.Label = uilabel(app.UIFigure);
app.Label.BackgroundColor = [0 0 1];
app.Label.HorizontalAlignment = 'center';
app.Label.FontName = '黑体';
app.Label.FontSize = 36;
app.Label.FontWeight = 'bold';
app.Label.FontColor = [1 1 1];
app.Label.Position = [0 420 640 60];
app.Label.Text = '杨氏双缝干涉仿真动画';
% Create Panel_2
app.Panel_2 = uipanel(app.UIFigure);
app.Panel_2.Title = '参数输入(单位:微米)';
app.Panel_2.Position = [415 250 200 150];
% Create EditFieldLabel
app.EditFieldLabel = uilabel(app.Panel_2);
app.EditFieldLabel.HorizontalAlignment = 'right';
app.EditFieldLabel.Position = [20 98 25 22];
app.EditFieldLabel.Text = '波长';
% Create EditField
app.EditField = uieditfield(app.Panel_2, 'numeric');
app.EditField.Limits = [0.39 0.76];
app.EditField.ValueChangedFcn = createCallbackFcn(app, @EditFieldValueChanged, true);
app.EditField.HorizontalAlignment = 'center';
app.EditField.Position = [60 99 120 20];
app.EditField.Value = 0.6;
% Create dEditFieldLabel
app.dEditFieldLabel = uilabel(app.Panel_2);
app.dEditFieldLabel.HorizontalAlignment = 'right';
app.dEditFieldLabel.Position = [20 69 25 22];
app.dEditFieldLabel.Text = 'd';
% Create dEditField
app.dEditField = uieditfield(app.Panel_2, 'numeric');
app.dEditField.ValueChangedFcn = createCallbackFcn(app, @dEditFieldValueChanged, true);
app.dEditField.HorizontalAlignment = 'center';
app.dEditField.Position = [60 70 120 20];
app.dEditField.Value = 3;
% Create DEditFieldLabel
app.DEditFieldLabel = uilabel(app.Panel_2);
app.DEditFieldLabel.HorizontalAlignment = 'right';
app.DEditFieldLabel.Position = [20 39 25 22];
app.DEditFieldLabel.Text = 'D';
% Create DEditField
app.DEditField = uieditfield(app.Panel_2, 'numeric');
app.DEditField.ValueChangedFcn = createCallbackFcn(app, @DEditFieldValueChanged, true);
app.DEditField.HorizontalAlignment = 'center';
app.DEditField.Position = [60 40 120 20];
app.DEditField.Value = 10;
% Create xEditFieldLabel
app.xEditFieldLabel = uilabel(app.Panel_2);
app.xEditFieldLabel.HorizontalAlignment = 'right';
app.xEditFieldLabel.Position = [20 9 25 22];
app.xEditFieldLabel.Text = 'x';
% Create xEditField
app.xEditField = uieditfield(app.Panel_2, 'numeric');
app.xEditField.ValueChangedFcn = createCallbackFcn(app, @xEditFieldValueChanged, true);
app.xEditField.HorizontalAlignment = 'center';
app.xEditField.Position = [60 10 120 20];
app.xEditField.Value = 5;
% Create Panel
app.Panel = uipanel(app.UIFigure);
app.Panel.Title = '双缝干涉动画';
app.Panel.Position = [415 65 200 170];
% Create Tree
app.Tree = uitree(app.Panel, 'checkbox');
app.Tree.FontSize = 5;
app.Tree.Position = [0 0 125 75];
% Create Node
app.Node = uitreenode(app.Tree);
app.Node.Text = '干涉线';
% Create Node_2
app.Node_2 = uitreenode(app.Node);
app.Node_2.Text = '相长线';
% Create Node_3
app.Node_3 = uitreenode(app.Node);
app.Node_3.Text = '相消线';
% Create Tree_2
app.Tree_2 = uitree(app.Panel, 'checkbox');
app.Tree_2.FontSize = 5;
app.Tree_2.Position = [0 75 125 75];
% Create Node_4
app.Node_4 = uitreenode(app.Tree_2);
app.Node_4.Text = '颜色选择';
% Create Node_5
app.Node_5 = uitreenode(app.Node_4);
app.Node_5.Text = '波峰:红色';
% Create Node_6
app.Node_6 = uitreenode(app.Node_4);
app.Node_6.Text = '波谷:蓝色';
% Create Button
app.Button = uibutton(app.Panel, 'push');
app.Button.ButtonPushedFcn = createCallbackFcn(app, @ButtonPushed, true);
app.Button.Position = [138 99 50 30];
app.Button.Text = '重置';
% Create Button_2
app.Button_2 = uibutton(app.Panel, 'push');
app.Button_2.ButtonPushedFcn = createCallbackFcn(app, @Button_2Pushed, true);
app.Button_2.Position = [138 56 50 30];
app.Button_2.Text = '播放';
% Create Button_3
app.Button_3 = uibutton(app.Panel, 'push');
app.Button_3.ButtonPushedFcn = createCallbackFcn(app, @Button_3Pushed, true);
app.Button_3.Position = [138 13 50 30];
app.Button_3.Text = '仿真';
% Create Label_2
app.Label_2 = uilabel(app.UIFigure);
app.Label_2.HorizontalAlignment = 'right';
app.Label_2.Position = [415 31 53 22];
app.Label_2.Text = '播放速度';
% Create Slider
app.Slider = uislider(app.UIFigure);
app.Slider.Limits = [0 1];
app.Slider.Position = [489 40 115 3];
app.Slider.Value = 0.2;
% Create ContextMenu
app.ContextMenu = uicontextmenu(app.UIFigure);
% Create Menu
app.Menu = uimenu(app.ContextMenu);
app.Menu.MenuSelectedFcn = createCallbackFcn(app, @MenuSelected, true);
app.Menu.Text = '关于';
% Assign app.ContextMenu
app.UIFigure.ContextMenu = app.ContextMenu;
% Show the figure after all components are created
app.UIFigure.Visible = 'on';
end
end
% App creation and deletion
methods (Access = public)
% Construct app
function app = DoubleSlitInterference
% Create UIFigure and components
createComponents(app)
% Register the app with App Designer
registerApp(app, app.UIFigure)
% Execute the startup function
runStartupFcn(app, @startupFcn)
if nargout == 0
clear app
end
end
% Code that executes before app deletion
function delete(app)
% Delete UIFigure when app is deleted
delete(app.UIFigure)
end
end
end
ntextMenu);
app.Menu.MenuSelectedFcn = createCallbackFcn(app, @MenuSelected, true);
app.Menu.Text = '关于';
% Assign app.ContextMenu
app.UIFigure.ContextMenu = app.ContextMenu;
% Show the figure after all components are created
app.UIFigure.Visible = 'on';
end
end
% App creation and deletion
methods (Access = public)
% Construct app
function app = DoubleSlitInterference
% Create UIFigure and components
createComponents(app)
% Register the app with App Designer
registerApp(app, app.UIFigure)
% Execute the startup function
runStartupFcn(app, @startupFcn)
if nargout == 0
clear app
end
end
% Code that executes before app deletion
function delete(app)
% Delete UIFigure when app is deleted
delete(app.UIFigure)
end
end
end