Signals and systems · In the CV
Convolution and Correlation Visualizer
Developed an app that animates the convolution and correlation of two signals step by step.
- MATLAB
- App Designer
About
A MATLAB GUI app for convolving continuous-time (CT) and discrete-time (DT) signals. It animates the two signals as they convolve so the result is easy to follow, and it can also show the correlation between two signals.
How to run
- Download and unzip the archive, or save the three .m files in one folder. Open ConvolutionModeSelectorApp.m in MATLAB and run it. Runs in MATLAB R2023b or newer.
Source code
ContinuousConvolutionApp.m
classdef ContinuousConvolutionApp < matlab.apps.AppBase
properties (Access = public)
UIFigure matlab.ui.Figure
xInput matlab.ui.control.EditField
hInput matlab.ui.control.EditField
xMinInput matlab.ui.control.NumericEditField
xMaxInput matlab.ui.control.NumericEditField
hMinInput matlab.ui.control.NumericEditField
hMaxInput matlab.ui.control.NumericEditField
ConvolveButton matlab.ui.control.Button
AnimateButton matlab.ui.control.Button
StopButton matlab.ui.control.Button
RestartButton matlab.ui.control.Button
CorrelationButton matlab.ui.control.Button
SpeedSliderLabel matlab.ui.control.Label
SpeedSlider matlab.ui.control.Slider
xAxes matlab.ui.control.UIAxes
hAxes matlab.ui.control.UIAxes
yAxes matlab.ui.control.UIAxes
animAxes matlab.ui.control.UIAxes
xDropDown matlab.ui.control.DropDown
hDropDown matlab.ui.control.DropDown
PredefinedLabel matlab.ui.control.Label
ManualLabel matlab.ui.control.Label
RangeLabel matlab.ui.control.Label
xLabel matlab.ui.control.Label
hLabel matlab.ui.control.Label
SwitchModeButton matlab.ui.control.Button
end
properties (Access = private)
StopFlag = false
h_rev = []
tau = []
x_t = []
t_x = []
h_t = []
t_h = []
y_t = []
t_y = []
end
methods (Access = private)
function [x, t_x, h, t_h, isValid] = getInputs(app)
isValid = false;
% Process x(t)
try
t_start = app.xMinInput.Value;
t_end = app.xMaxInput.Value;
if t_start >= t_end
errordlg('x(t) start must be less than end');
return;
end
t_x = linspace(t_start, t_end, 1000);
t = t_x;
if strcmpi(strtrim(app.xInput.Value), 'Impulse')
x = zeros(size(t_x));
[~, idx0] = min(abs(t_x));
x(idx0) = 1 / (t_x(2) - t_x(1)); % Approximate delta with area 1
else
x = double(eval(app.xInput.Value));
end
if length(x) ~= length(t_x)
errordlg('x(t) length mismatch');
return;
end
catch ME
errordlg(['Invalid x(t): ' ME.message]);
return;
end
% Process h(t)
try
t_start = app.hMinInput.Value;
t_end = app.hMaxInput.Value;
if t_start >= t_end
errordlg('h(t) start must be less than end');
return;
end
t_h = linspace(t_start, t_end, 1000);
t = t_h;
if strcmpi(strtrim(app.hInput.Value), 'Impulse')
h = zeros(size(t_h));
[~, idx0] = min(abs(t_h));
h(idx0) = 1 / (t_h(2) - t_h(1)); % Approximate delta
else
h = double(eval(app.hInput.Value));
end
if length(h) ~= length(t_h)
errordlg('h(t) length mismatch');
return;
end
catch ME
errordlg(['Invalid h(t): ' ME.message]);
return;
end
% Save results
app.t_x = t_x;
app.x_t = x;
app.t_h = t_h;
app.h_t = h;
isValid = true;
end
function plotFixedSignals(app)
plot(app.xAxes, app.t_x, app.x_t, 'y', 'LineWidth', 1.5);
app.xAxes.Color = 'k';
title(app.xAxes, 'x(t)', 'Color', 'w');
grid(app.xAxes, 'on');
app.xAxes.XColor = 'w'; app.xAxes.YColor = 'w';
xlim(app.xAxes, [min(app.t_x)-0.5, max(app.t_x)+0.5]);
app.h_rev = fliplr(app.h_t);
app.tau = -fliplr(app.t_h);
plot(app.hAxes, app.tau, app.h_rev, 'y', 'LineWidth', 1.5);
app.hAxes.Color = 'k';
title(app.hAxes, 'h(-τ)', 'Color', 'w');
grid(app.hAxes, 'on');
app.hAxes.XColor = 'w'; app.hAxes.YColor = 'w';
xlim(app.hAxes, [min(app.tau)-0.5, max(app.tau)+0.5]);
end
function ConvolveButtonPushed(app, ~, ~)
[x, t_x, h, t_h, isValid] = app.getInputs();
if ~isValid, return; end
dt = t_x(2) - t_x(1);
isImpulseX = strcmpi(strtrim(app.xInput.Value), 'Impulse');
isImpulseH = strcmpi(strtrim(app.hInput.Value), 'Impulse');
% Determine output y(t) based on impulse presence
if isImpulseX && ~isImpulseH
% x(t) = delta(t), so y(t) = h(t)
conv_result = h;
t_conv = t_h;
elseif ~isImpulseX && isImpulseH
% h(t) = delta(t), so y(t) = x(t)
conv_result = x;
t_conv = t_x;
else
% Normal convolution
conv_result = conv(x, h, 'full') * dt;
t_conv = t_x(1) + t_h(1) + (0:length(conv_result)-1) * dt;
end
app.y_t = conv_result;
app.t_y = t_conv;
% Plot result
plot(app.yAxes, app.t_y, app.y_t, 'y', 'LineWidth', 1.5);
app.yAxes.Color = 'k';
title(app.yAxes, 'x(t)*h(t)', 'Color', 'w');
grid(app.yAxes, 'on');
app.yAxes.XColor = 'w';
app.yAxes.YColor = 'w';
xlim(app.yAxes, [min(app.t_y)-0.5, max(app.t_y)+0.5]);
app.plotFixedSignals();
end
% Correlation button callback function
function CorrelationButtonPushed(app, ~, ~)
[x, t_x, h, t_h, isValid] = app.getInputs();
if ~isValid, return; end
dt = t_x(2) - t_x(1);
% Computing correlation via convolution with time-reversed signal
correlation = conv(x, h(end:-1:1), 'full') * dt;
t_corr = t_x(1) - t_h(end) + (0:length(correlation)-1)*dt;
plot(app.yAxes, t_corr, correlation, 'c', 'LineWidth', 1.5);
app.yAxes.Color = 'k';
title(app.yAxes, 'Correlation: R_{xh}(\tau)', 'Color', 'w');
grid(app.yAxes, 'on');
app.yAxes.XColor = 'w'; app.yAxes.YColor = 'w';
xlim(app.yAxes, [min(t_corr)-0.5, max(t_corr)+0.5]);
app.plotFixedSignals();
end
function AnimateButtonPushed(app, ~, ~)
app.StopFlag = false;
[x, t_x, h, t_h, isValid] = app.getInputs();
if ~isValid, return; end
dt = t_x(2) - t_x(1);
conv_result = conv(x, h, 'full') * dt;
t_conv = t_x(1) + t_h(1) + (0:length(conv_result)-1)*dt;
app.y_t = conv_result;
app.t_y = t_conv;
num_frames = length(t_conv);
cla(app.animAxes);
cla(app.yAxes);
plot(app.yAxes, app.t_y, app.y_t, 'y', 'LineWidth', 1.5);
title(app.yAxes, 'x(t)*h(t)', 'Color', 'w');
app.yAxes.Color = 'k';
grid(app.yAxes, 'on');
app.yAxes.XColor = 'w';
app.yAxes.YColor = 'w';
xlim(app.yAxes, [min(app.t_y)-0.5, max(app.t_y)+0.5]);
hold(app.yAxes, 'on');
tau_min = min([t_x, t_h]);
tau_max = max([t_x, t_h]);
tau = linspace(tau_min, tau_max, 1000);
x_tau = interp1(t_x, x, tau, 'linear', 0);
i = 1;
while i <= num_frames
if app.StopFlag, break; end
t_val = t_conv(i);
query_points = t_val - tau;
h_shifted = interp1(t_h, h, query_points, 'linear', 0);
cla(app.animAxes);
hold(app.animAxes, 'on');
plot(app.animAxes, tau, x_tau, 'y', 'LineWidth', 1.5);
plot(app.animAxes, tau, h_shifted, 'r', 'LineWidth', 1.5);
plot(app.yAxes, t_val, app.y_t(i), 'ro', 'MarkerSize', 8, 'MarkerFaceColor', 'r');
title(app.animAxes, sprintf('Animation: t = %.2f', t_val), 'Color', 'w');
app.animAxes.Color = 'k';
grid(app.animAxes, 'on');
app.animAxes.XColor = 'w';
app.animAxes.YColor = 'w';
xlim(app.animAxes, [tau_min-0.5, tau_max+0.5]);
min_val = min([min(x), min(h)]);
max_val = max([max(x), max(h)]);
if min_val == max_val
padding = 1;
else
padding = 0.2*(max_val - min_val);
end
ylim(app.animAxes, [min_val - padding, max_val + padding]);
hold(app.animAxes, 'off');
drawnow;
while app.SpeedSlider.Value == 0 && ~app.StopFlag
pause(0.05);
end
if app.StopFlag, break; end
s = app.SpeedSlider.Value;
step_time = 0.1 * s;
step_samples = round(step_time / dt);
if step_samples < 1
step_samples = 1;
end
i = i + step_samples;
pause(0.01);
end
hold(app.yAxes, 'off');
end
function StopButtonPushed(app, ~, ~)
app.StopFlag = true;
end
function RestartButtonPushed(app, ~, ~)
cla(app.animAxes);
cla(app.yAxes);
cla(app.hAxes);
cla(app.xAxes);
app.StopFlag = false;
end
function updatePredefinedSignal(app, dropdown, target, minField, maxField)
switch dropdown.Value
case 'Impulse'
target.Value = 'Impulse';
minField.Value = -1;
maxField.Value = 1;
case 'Step'
target.Value = '(t>=0)';
minField.Value = -2;
maxField.Value = 2;
case 'Triangular'
target.Value = 'max(0,1-abs(t))';
minField.Value = -1;
maxField.Value = 1;
case 'Rectangular'
target.Value = '((t>=-0.5)&(t<=0.5))';
minField.Value = -1;
maxField.Value = 1;
case 'Sawtooth'
target.Value = 'mod(t,1)';
minField.Value = 0;
maxField.Value = 3;
end
end
function xDropDownChanged(app, ~, ~)
app.updatePredefinedSignal(app.xDropDown, app.xInput, app.xMinInput, app.xMaxInput);
end
function hDropDownChanged(app, ~, ~)
app.updatePredefinedSignal(app.hDropDown, app.hInput, app.hMinInput, app.hMaxInput);
end
function SwitchModeButtonPushed(app, ~, ~)
delete(app.UIFigure);
DiscreteConvolutionApp();
end
end
methods (Access = public)
function app = ContinuousConvolutionApp
app.createComponents();
registerApp(app, app.UIFigure);
end
function delete(app)
delete(app.UIFigure);
end
end
methods (Access = private)
function createComponents(app)
app.UIFigure = uifigure('Name', 'Continuous Convolution Visualizer','Position', [100 100 970 650],'Color', [0.2 0.2 0.2]);
app.ManualLabel = uilabel(app.UIFigure,'Text', 'Manual Signal','Position', [50 610 100 22],'FontColor', 'w','FontWeight', 'bold');
app.xLabel = uilabel(app.UIFigure,'Text', 'x(t):','Position', [50 580 30 22],'FontColor', 'w');
app.xInput = uieditfield(app.UIFigure, 'text','Position', [80 580 100 22],'Value', '(t==0)','BackgroundColor', [0.3 0.3 0.3],'FontColor', 'w');
app.hLabel = uilabel(app.UIFigure, 'Text', 'h(t):', 'Position', [50 540 30 22],'FontColor', 'w');
app.hInput = uieditfield(app.UIFigure, 'text','Position', [80 540 100 22], 'Value', '(t>=0)', 'BackgroundColor', [0.3 0.3 0.3],'FontColor', 'w');
app.RangeLabel = uilabel(app.UIFigure,'Text', 'Time Range','Position', [200 610 80 22], 'FontColor', 'w','FontWeight', 'bold');
uilabel(app.UIFigure, 'Text', 'Min t:', 'Position', [200 580 45 22], 'FontColor', 'w');
app.xMinInput = uieditfield(app.UIFigure, 'numeric','Position', [250 580 50 22], 'Value', -1,'BackgroundColor', [0.3 0.3 0.3],'FontColor', 'w');
uilabel(app.UIFigure, 'Text', 'Max t:', 'Position', [310 580 45 22], 'FontColor', 'w');
app.xMaxInput = uieditfield(app.UIFigure, 'numeric', 'Position', [360 580 50 22],'Value', 1,'BackgroundColor', [0.3 0.3 0.3],'FontColor', 'w');
uilabel(app.UIFigure, 'Text', 'Min t:', 'Position', [200 540 45 22], 'FontColor', 'w');
app.hMinInput = uieditfield(app.UIFigure, 'numeric','Position', [250 540 50 22], 'Value', -2, 'BackgroundColor', [0.3 0.3 0.3],'FontColor', 'w');
uilabel(app.UIFigure, 'Text', 'Max t:', 'Position', [310 540 45 22], 'FontColor', 'w');
app.hMaxInput = uieditfield(app.UIFigure, 'numeric','Position', [360 540 50 22],'Value', 2, 'BackgroundColor', [0.3 0.3 0.3],'FontColor', 'w');
app.PredefinedLabel = uilabel(app.UIFigure,'Text', 'Predefined Signal', 'Position', [500 610 120 22],'FontColor', 'w','FontWeight', 'bold');
uilabel(app.UIFigure, 'Text', 'x(t):', 'Position', [500 580 40 22], 'FontColor', 'w');
app.xDropDown = uidropdown(app.UIFigure,'Position', [540 580 120 22],'Items', {'Impulse', 'Step', 'Triangular', 'Rectangular', 'Sawtooth'},'ValueChangedFcn', @(~,~) app.xDropDownChanged(),'BackgroundColor', [0.3 0.3 0.3],'FontColor', 'w','FontAngle','italic');
uilabel(app.UIFigure, 'Text', 'h(t):', 'Position', [500 540 40 22], 'FontColor', 'w');
app.hDropDown = uidropdown(app.UIFigure,'Position', [540 540 120 22],'Items', {'Impulse', 'Step', 'Triangular', 'Rectangular', 'Sawtooth'},'Value', 'Step','ValueChangedFcn', @(~,~) app.hDropDownChanged(),'BackgroundColor', [0.3 0.3 0.3],'FontColor', 'w','FontAngle','italic');
% Button positions
app.ConvolveButton = uibutton(app.UIFigure, 'push','Text', 'Convolve','Position', [700 580 80 25],'ButtonPushedFcn', @(~,~) app.ConvolveButtonPushed(),'BackgroundColor', [0.4 0.4 0.4],'FontColor', 'w');
app.AnimateButton = uibutton(app.UIFigure, 'push', 'Text', 'Animate', 'Position', [790 580 80 25],'ButtonPushedFcn', @(~,~) app.AnimateButtonPushed(), 'BackgroundColor', [0.4 0.4 0.4],'FontColor', 'w');
app.CorrelationButton = uibutton(app.UIFigure, 'push', 'Text', 'Correlation', 'Position', [880 580 80 25], 'ButtonPushedFcn', @(~,~) app.CorrelationButtonPushed(), 'BackgroundColor', [0.4 0.4 0.4], 'FontColor', 'w'); % NEW
app.StopButton = uibutton(app.UIFigure, 'push', 'Text', 'Stop', 'Position', [700 540 80 25],'ButtonPushedFcn', @(~,~) app.StopButtonPushed(), 'BackgroundColor', [0.4 0.4 0.4],'FontColor', 'w');
app.RestartButton = uibutton(app.UIFigure, 'push','Text', 'Restart','Position', [790 540 80 25],'ButtonPushedFcn', @(~,~) app.RestartButtonPushed(), 'BackgroundColor', [0.4 0.4 0.4],'FontColor', 'w');
app.SpeedSliderLabel = uilabel(app.UIFigure, 'Text', 'Animation Speed','FontAngle','italic','Position', [670 130 100 22],'FontColor', 'w');
app.SpeedSlider = uislider(app.UIFigure,'Position', [670 120 180 3], 'Limits', [0 1],'Value', 0.5,'MajorTicks', [0 0.2 0.4 0.6 0.8 1],'MajorTickLabels', {'0', '0.2', '0.4', '0.6', '0.8', '1'},'FontColor','w');
app.SwitchModeButton = uibutton(app.UIFigure, 'push', 'Text', 'Switch to Discrete', 'Position', [50 100 180 25],'ButtonPushedFcn', @(~,~) app.SwitchModeButtonPushed(), 'BackgroundColor', [0.4 0.4 0.4], 'FontColor', 'w');
app.xAxes = uiaxes(app.UIFigure,'Position', [40 380 360 150], 'Box', 'on');
title(app.xAxes, 'x(t)','Color','w','FontName','Times new roman');
app.hAxes = uiaxes(app.UIFigure,'Position', [500 380 360 150],'Box', 'on');
title(app.hAxes, 'h(-τ)','Color','w','FontName','Times new roman');
app.yAxes = uiaxes(app.UIFigure,'Position', [40 180 360 150],'Box', 'on');
title(app.yAxes, 'x(t)*h(t)','Color','w','FontName','Times new roman');
app.animAxes = uiaxes(app.UIFigure,'Position', [500 180 360 150],'Box', 'on');
title(app.animAxes, 'Animation: x(τ) and h(t-τ)','Color','w','FontName','Times new roman');
axesList = [app.xAxes, app.hAxes, app.yAxes, app.animAxes];
for ax = axesList
ax.Color = 'k';
ax.XColor = 'w';
ax.YColor = 'w';
ax.GridColor = [0.5 0.5 0.5];
ax.GridAlpha = 0.3;
ax.FontWeight = 'bold';
grid(ax, 'on');
end
app.xDropDown.Value = 'Impulse';
app.hDropDown.Value = 'Step';
app.xDropDownChanged();
app.hDropDownChanged();
end
end
end ConvolutionModeSelectorApp.m
classdef ConvolutionModeSelectorApp < matlab.apps.AppBase
properties
UIFigure matlab.ui.Figure
ModeDropDown matlab.ui.control.DropDown
LaunchButton matlab.ui.control.Button
end
methods (Access = private)
function launchMode(app,~,~)
selectedMode = app.ModeDropDown.Value;
delete(app.UIFigure); %close selector window
switch selectedMode
case 'Discrete'
DiscreteConvolutionApp();
case 'Continuous'
ContinuousConvolutionApp();
end
end
function createComponents(app)
app.UIFigure = uifigure('Name','Select Mode','Position', [600 150 800 500],'Color', [0.2 0.2 0.2]); %dark theme
app.ModeDropDown = uidropdown(app.UIFigure,'Items', {'Discrete', 'Continuous'},'Position', [270 260 100 30],'BackgroundColor', [0.3 0.3 0.3],'FontColor', 'w');
app.LaunchButton = uibutton(app.UIFigure,'push','Text', 'Launch','Position',[270 200 100 30],'ButtonPushedFcn', @app.launchMode,'FontColor', [0.1 0.1 0.1]);
end
end
methods (Access = public)
function app = ConvolutionModeSelectorApp
createComponents(app);
end
end
endDiscreteConvolutionApp.m
classdef DiscreteConvolutionApp < matlab.apps.AppBase
properties (Access = public)
UIFigure matlab.ui.Figure
xInput matlab.ui.control.EditField
hInput matlab.ui.control.EditField
xMinInput matlab.ui.control.NumericEditField
xMaxInput matlab.ui.control.NumericEditField
hMinInput matlab.ui.control.NumericEditField
hMaxInput matlab.ui.control.NumericEditField
ConvolveButton matlab.ui.control.Button
AnimateButton matlab.ui.control.Button
StopButton matlab.ui.control.Button
RestartButton matlab.ui.control.Button
CorrelationButton matlab.ui.control.Button
SpeedSliderLabel matlab.ui.control.Label
SpeedSlider matlab.ui.control.Slider
xAxes matlab.ui.control.UIAxes
hAxes matlab.ui.control.UIAxes
yAxes matlab.ui.control.UIAxes
animAxes matlab.ui.control.UIAxes
xDropDown matlab.ui.control.DropDown
hDropDown matlab.ui.control.DropDown
PredefinedLabel matlab.ui.control.Label
ManualLabel matlab.ui.control.Label
RangeLabel matlab.ui.control.Label
xLabel matlab.ui.control.Label
hLabel matlab.ui.control.Label
SwitchModeButton matlab.ui.control.Button
end
properties (Access = private)
StopFlag = false
h_rev = []
nh_rev = []
end
methods (Access = private)
function [x, nx, h, nh, isValid] = getInputs(app)
isValid = false;
try
nx = app.xMinInput.Value:app.xMaxInput.Value;
n = nx; % define n for evaluation
x = eval(app.xInput.Value);
if length(x) ~= length(nx)
errordlg('x[n] length mismatch'); return;
end
catch ME
errordlg(['Invalid x[n]: ' ME.message]);
return;
end
try
nh = app.hMinInput.Value:app.hMaxInput.Value;
n = nh; % define n for evaluation
h = eval(app.hInput.Value);
if length(h) ~= length(nh)
errordlg('h[n] length mismatch'); return;
end
catch ME
errordlg(['Invalid h[n]: ' ME.message]);
return;
end
isValid = true;
end
function plotFixedSignals(app, nx, x, nh, h)
stem(app.xAxes, nx, x, 'filled', 'LineWidth', 1.2, 'Color', 'yellow');
app.xAxes.Color = 'k';
title(app.xAxes, 'x[n]', 'Color', 'w');
grid(app.xAxes, 'on');
app.xAxes.XColor = 'w'; app.xAxes.YColor = 'w';
xlim(app.xAxes, [min(nx)-1, max(nx)+1]);
app.h_rev = fliplr(h);
app.nh_rev = -fliplr(nh);
stem(app.hAxes, app.nh_rev, app.h_rev, 'filled', 'LineWidth', 1.2, 'Color', 'yellow');
app.hAxes.Color = 'k';
title(app.hAxes, 'h[-n]', 'Color', 'w');
grid(app.hAxes, 'on');
app.hAxes.XColor = 'w'; app.hAxes.YColor = 'w';
xlim(app.hAxes, [min(app.nh_rev)-1, max(app.nh_rev)+1]);
end
function ConvolveButtonPushed(app, ~, ~)
[x, nx, h, nh, isValid] = app.getInputs();
if ~isValid, return; end
y = conv(x, h);
ny = nx(1)+nh(1):nx(end)+nh(end);
stem(app.yAxes, ny, y, 'filled', 'Color', 'yellow');
app.yAxes.Color = 'k';
title(app.yAxes, 'x[n]*h[n]', 'Color', 'w');
grid(app.yAxes, 'on');
app.yAxes.XColor = 'w'; app.yAxes.YColor = 'w';
xlim(app.yAxes, [min(ny)-1, max(ny)+1]);
app.plotFixedSignals(nx, x, nh, h);
end
% correlation button callback function
function CorrelationButtonPushed(app, ~, ~)
[x, nx, h, nh, isValid] = app.getInputs();
if ~isValid, return; end
% compute correlation
correlation = conv(x, h(end:-1:1));
n_corr = (nx(1) - nh(end)) : (nx(end) - nh(1));
% plot
stem(app.yAxes, n_corr, correlation, 'filled', 'Color', 'c');
title(app.yAxes, 'Correlation: R_{xh}[n]', 'Color', 'w');
grid(app.yAxes, 'on');
app.yAxes.XColor = 'w'; app.yAxes.YColor = 'w';
xlim(app.yAxes, [min(n_corr)-1, max(n_corr)+1]);
% plot the fixed signals
app.plotFixedSignals(nx, x, nh, h);
end
function AnimateButtonPushed(app, ~, ~) % animate button
app.StopFlag = false;
[x, nx, h, nh, isValid] = app.getInputs();
if ~isValid, return; end
h_rev = fliplr(h);
nh_rev = -fliplr(nh);
app.h_rev = h_rev;
app.nh_rev = nh_rev;
ny = nx(1)+nh(1):nx(end)+nh(end);
y = conv(x, h);
total_time = interp1([0, 0.8, 1], [20, 5, 3], app.SpeedSlider.Value, 'linear', 'extrap');
delay = total_time / length(ny);
cla(app.animAxes);
cla(app.yAxes);
stem(app.yAxes, ny, zeros(size(ny)), 'filled', 'Color', 'yellow');
title(app.yAxes, 'x[n]*h[n]', 'Color', 'w');
app.yAxes.Color = 'k';
grid(app.yAxes, 'on');
app.yAxes.XColor = 'w';
app.yAxes.YColor = 'w';
xlim(app.yAxes, [min(ny)-1, max(ny)+1]);
hold(app.yAxes, 'on');
for i = 1:length(ny)
if app.StopFlag, break; end
n = ny(i);
shift = nh_rev + n;
cla(app.animAxes);
hold(app.animAxes, 'on');
stem(app.animAxes, nx, x, 'filled', 'LineWidth', 1.2, 'Color', 'yellow');
stem(app.animAxes, shift, h_rev, 'filled', 'LineWidth', 1.2, 'Color', 'r');
title(app.animAxes, sprintf('Animation: n = %d', n), 'Color', 'w');
app.animAxes.Color = 'k';
grid(app.animAxes, 'on');
app.animAxes.XColor = 'w';
app.animAxes.YColor = 'w';
xlim(app.animAxes, [min([nx, shift])-1, max([nx, shift])+1]);
hold(app.animAxes, 'off');
stem(app.yAxes, ny(i), y(i), 'filled', 'Color', 'red', 'MarkerSize', 8);
drawnow;
pause(delay);
end
hold(app.yAxes, 'off');
end
function StopButtonPushed(app, ~, ~)
app.StopFlag = true;
end
function RestartButtonPushed(app, ~, ~)
cla(app.animAxes);
cla(app.yAxes);
app.StopFlag = false;
end
function updatePredefinedSignal(app, dropdown, target, minField, maxField)
switch dropdown.Value
case 'Impulse'
target.Value = 'n==0';
minField.Value = 0;
maxField.Value = 0;
case 'Step'
target.Value = 'n>=0';
minField.Value = -2;
maxField.Value = 2;
case 'Triangular'
target.Value = '1-abs(n)';
minField.Value = -1;
maxField.Value = 1;
case 'Rectangular'
target.Value = '(n>=-0.5)&(n<=0.5)';
minField.Value = -1;
maxField.Value = 1;
case 'Sawtooth'
target.Value = 'mod(n,1)';
minField.Value = 0;
maxField.Value = 3;
end
end
function xDropDownChanged(app, ~, ~)
app.updatePredefinedSignal(app.xDropDown, app.xInput, app.xMinInput, app.xMaxInput);
end
function hDropDownChanged(app, ~, ~)
app.updatePredefinedSignal(app.hDropDown, app.hInput, app.hMinInput, app.hMaxInput);
end
function SwitchModeButtonPushed(app, ~, ~)
delete(app.UIFigure);
ContinuousConvolutionApp();
end
end
methods (Access = public)
function app = DiscreteConvolutionApp
app.createComponents();
registerApp(app, app.UIFigure);
end
function delete(app)
delete(app.UIFigure);
end
end
methods (Access = private)
function createComponents(app)
app.UIFigure = uifigure('Name', 'Discrete Convolution Visualizer','Position', [100 100 970 650],'Color', [0.2 0.2 0.2]);
app.ManualLabel = uilabel(app.UIFigure,'Text', 'Manual Signal','Position', [50 610 100 22],'FontColor', 'w', 'FontWeight', 'bold');
app.xLabel = uilabel(app.UIFigure,'Text', 'x[n]:','Position', [50 580 30 22],'FontColor', 'w');
app.xInput = uieditfield(app.UIFigure, 'text','Position', [80 580 100 22],'Value', 'n==0','BackgroundColor', [0.3 0.3 0.3],'FontColor', 'w');
app.hLabel = uilabel(app.UIFigure,'Text', 'h[n]:','Position', [50 540 30 22],'FontColor', 'w');
app.hInput = uieditfield(app.UIFigure, 'text','Position', [80 540 100 22],'Value', 'n>=0','BackgroundColor', [0.3 0.3 0.3],'FontColor', 'w');
app.RangeLabel = uilabel(app.UIFigure, 'Text', 'Index Range', 'Position', [200 610 80 22], 'FontColor', 'w','FontWeight', 'bold');
uilabel(app.UIFigure, 'Text', 'Min n:', 'Position', [200 580 45 22], 'FontColor', 'w');
app.xMinInput = uieditfield(app.UIFigure, 'numeric', 'Position', [250 580 50 22],'Value', 0, 'BackgroundColor', [0.3 0.3 0.3],'FontColor', 'w');
uilabel(app.UIFigure, 'Text', 'Max n:', 'Position', [310 580 45 22], 'FontColor', 'w');
app.xMaxInput = uieditfield(app.UIFigure, 'numeric','Position', [360 580 50 22],'Value', 0,'BackgroundColor', [0.3 0.3 0.3],'FontColor', 'w');
uilabel(app.UIFigure, 'Text', 'Min n:', 'Position', [200 540 45 22], 'FontColor', 'w');
app.hMinInput = uieditfield(app.UIFigure, 'numeric', 'Position', [250 540 50 22],'Value', -2,'BackgroundColor', [0.3 0.3 0.3],'FontColor', 'w');
uilabel(app.UIFigure, 'Text', 'Max n:', 'Position', [310 540 45 22], 'FontColor', 'w');
app.hMaxInput = uieditfield(app.UIFigure, 'numeric','Position', [360 540 50 22],'Value', 2,'BackgroundColor', [0.3 0.3 0.3],'FontColor', 'w');
app.PredefinedLabel = uilabel(app.UIFigure, 'Text', 'Predefined Signal','Position', [500 610 120 22], 'FontColor', 'w', 'FontWeight', 'bold');
uilabel(app.UIFigure, 'Text', 'x[n]:', 'Position', [500 580 40 22], 'FontColor', 'w');
app.xDropDown = uidropdown(app.UIFigure,'Position', [540 580 120 22], 'Items', {'Impulse', 'Step', 'Triangular', 'Rectangular', 'Sawtooth'}, 'ValueChangedFcn', @(~,~) app.xDropDownChanged(),'BackgroundColor', [0.3 0.3 0.3],'FontColor', 'w','FontAngle','italic');
uilabel(app.UIFigure, 'Text', 'h[n]:', 'Position', [500 540 40 22], 'FontColor', 'w');
app.hDropDown = uidropdown(app.UIFigure,'Position', [540 540 120 22],'Items', {'Impulse', 'Step', 'Triangular', 'Rectangular', 'Sawtooth'},'Value', 'Step','ValueChangedFcn', @(~,~) app.hDropDownChanged(),'BackgroundColor', [0.3 0.3 0.3],'FontColor', 'w','FontAngle','italic');
% Button positions
app.ConvolveButton = uibutton(app.UIFigure, 'push','Text', 'Convolve','Position', [700 580 80 25],'ButtonPushedFcn', @(~,~) app.ConvolveButtonPushed(),'BackgroundColor', [0.4 0.4 0.4],'FontColor', 'w');
app.AnimateButton = uibutton(app.UIFigure, 'push', 'Text', 'Animate', 'Position', [790 580 80 25],'ButtonPushedFcn', @(~,~) app.AnimateButtonPushed(),'BackgroundColor', [0.4 0.4 0.4],'FontColor', 'w');
app.CorrelationButton = uibutton(app.UIFigure, 'push', 'Text', 'Correlation', 'Position', [880 580 80 25], 'ButtonPushedFcn', @(~,~) app.CorrelationButtonPushed(), 'BackgroundColor', [0.4 0.4 0.4], 'FontColor', 'w'); % NEW
app.StopButton = uibutton(app.UIFigure, 'push', 'Text', 'Stop', 'Position', [700 540 80 25], 'ButtonPushedFcn', @(~,~) app.StopButtonPushed(), 'BackgroundColor', [0.4 0.4 0.4],'FontColor', 'w');
app.RestartButton = uibutton(app.UIFigure, 'push','Text', 'Restart','Position', [790 540 80 25], 'ButtonPushedFcn', @(~,~) app.RestartButtonPushed(),'BackgroundColor', [0.4 0.4 0.4],'FontColor', 'w');
app.SpeedSliderLabel = uilabel(app.UIFigure,'Text', 'Animation Speed','FontAngle','italic','Position', [670 130 100 22],'FontColor', 'w');
app.SpeedSlider = uislider(app.UIFigure,'Position', [670 120 180 3],'Limits', [0 1],'Value', 0.5,'MajorTicks', [0 0.2 0.4 0.6 0.8 1],'MajorTickLabels', {'0', '0.2', '0.4', '0.6', '0.8', '1'},'FontColor','w');
app.SwitchModeButton = uibutton(app.UIFigure, 'push', 'Text', 'Switch to Continuous', 'Position', [50 100 180 25],'ButtonPushedFcn', @(~,~) app.SwitchModeButtonPushed(), 'BackgroundColor', [0.4 0.4 0.4], 'FontColor', 'w');
app.xAxes = uiaxes(app.UIFigure,'Position', [40 380 360 150], 'Box', 'on');
title(app.xAxes, 'x[n]','Color','w','FontName','Times new roman');
app.hAxes = uiaxes(app.UIFigure,'Position', [500 380 360 150],'Box', 'on');
title(app.hAxes, 'h[-n]','Color','w','FontName','Times new roman');
app.yAxes = uiaxes(app.UIFigure,'Position', [40 180 360 150],'Box', 'on');
title(app.yAxes, 'x[n]*h[n]','Color','w','FontName','Times new roman');
app.animAxes = uiaxes(app.UIFigure, 'Position', [500 180 360 150],'Box', 'on');
title(app.animAxes, 'Animation: x[k] and h[n-k]','Color','w','FontName','Times new roman');
axesList = [app.xAxes, app.hAxes, app.yAxes, app.animAxes];
for ax = axesList
ax.Color = 'k';
ax.XColor = 'w';
ax.YColor = 'w';
ax.GridColor = [0.5 0.5 0.5];
ax.GridAlpha = 0.3;
ax.FontWeight = 'bold';
grid(ax, 'on');
end
app.xDropDown.Value = 'Impulse';
app.hDropDown.Value = 'Step';
app.xDropDownChanged();
app.hDropDownChanged();
end
end
end