function ofdm_step_gui()
% 创建主界面
fig = figure('Name', 'OFDM系统分步仿真', 'Position', [100 100 1200 800], ...
'NumberTitle', 'off', 'Resize', 'off');
% 创建控制面板
ctrl_panel = uipanel('Title', '控制面板', 'Position', [0.02 0.02 0.25 0.96]);
% 创建结果展示区
results_panel = uipanel('Title', '仿真结果', 'Position', [0.28 0.02 0.7 0.96]);
% 初始化参数存储结构
handles = struct();
% 添加参数输入控件
uicontrol(ctrl_panel, 'Style', 'text', 'String', '子载波数量:', ...
'Position', [20 700 100 20], 'HorizontalAlignment', 'left');
handles.num_sc = uicontrol(ctrl_panel, 'Style', 'edit', 'String', '64', ...
'Position', [130 700 80 25]);
uicontrol(ctrl_panel, 'Style', 'text', 'String', '调制方式:', ...
'Position', [20 650 100 20], 'HorizontalAlignment', 'left');
handles.mod_type = uicontrol(ctrl_panel, 'Style', 'popupmenu', ...
'String', {'QPSK', '16QAM', 'BPSK'}, ...
'Position', [130 650 80 25], 'Value', 1);
uicontrol(ctrl_panel, 'Style', 'text', 'String', '循环前缀长度:', ...
'Position', [20 600 100 20], 'HorizontalAlignment', 'left');
handles.cp_len = uicontrol(ctrl_panel, 'Style', 'edit', 'String', '16', ...
'Position', [130 600 80 25]);
uicontrol(ctrl_panel, 'Style', 'text', 'String', '编码类型:', ...
'Position', [20 550 100 20], 'HorizontalAlignment', 'left');
handles.coding_type = uicontrol(ctrl_panel, 'Style', 'popupmenu', ...
'String', {'卷积编码', '无编码'}, ...
'Position', [130 550 80 25], 'Value', 1);
uicontrol(ctrl_panel, 'Style', 'text', 'String', '交织深度:', ...
'Position', [20 500 100 20], 'HorizontalAlignment', 'left');
handles.intlvr_depth = uicontrol(ctrl_panel, 'Style', 'edit', 'String', '100', ...
'Position', [130 500 80 25]);
uicontrol(ctrl_panel, 'Style', 'text', 'String', 'SNR(dB):', ...
'Position', [20 450 100 20], 'HorizontalAlignment', 'left');
handles.snr = uicontrol(ctrl_panel, 'Style', 'edit', 'String', '30', ...
'Position', [130 450 80 25]);
% 创建步骤按钮
step_buttons = {
'生成原始信号', @gen_data;
'编码', @encode_data;
'交织', @interleave_data;
'调制', @modulate_data;
'IFFT变换', @ifft_transform;
'加循环前缀', @add_cp;
'上变频', @upconvert;
'信道传输', @channel_transmission;
'接收处理', @receive_process;
'解调', @demodulate_data;
'解交织', @deinterleave_data;
'解码', @decode_data;
'误码率分析', @ber_analysis;
};
for i = 1:size(step_buttons, 1)
uicontrol(ctrl_panel, 'Style', 'pushbutton', ...
'String', step_buttons{i,1}, ...
'Position', [30 400-30*i 180 30], ...
'Callback', step_buttons{i,2}, ...
'Tag', ['btn_' num2str(i)]);
end
% 创建结果展示区域
ax_positions = {
[0.05 0.65 0.3 0.3], '原始信号';
[0.35 0.65 0.3 0.3], '编码后信号';
[0.65 0.65 0.3 0.3], '交织后信号';
[0.05 0.35 0.3 0.3], '调制星座图';
[0.35 0.35 0.3 0.3], 'OFDM时域信号';
[0.65 0.35 0.3 0.3], '加CP后频谱';
[0.05 0.05 0.3 0.3], '天线输出频谱';
[0.35 0.05 0.3 0.3], '接收端星座图';
[0.65 0.05 0.3 0.3], '误码率分析';
};
handles.axes = gobjects(size(ax_positions, 1), 1);
for i = 1:size(ax_positions, 1)
handles.axes(i) = axes(results_panel, 'Position', ax_positions{i,1});
title(handles.axes(i), ax_positions{i,2});
grid(handles.axes(i), 'on');
end
% 添加状态文本
handles.status_text = uicontrol(results_panel, 'Style', 'text', ...
'String', '准备就绪', ...
'Position', [10 730 300 20], ...
'HorizontalAlignment', 'left', ...
'FontSize', 10);
% 存储数据供回调使用
handles.data = struct(); % 存储中间数据
guidata(fig, handles);
end
%% 生成原始信号回调函数
function gen_data(hObject, ~)
handles = guidata(hObject);
% 更新状态
set(handles.status_text, 'String', '正在生成原始信号...');
drawnow;
% 获取参数
N = str2double(get(handles.num_sc, 'String')); % 子载波数量
depth = str2double(get(handles.intlvr_depth, 'String')); % 交织深度
% 生成原始信号 (确保有足够数据)
total_bits = max(10000, depth * ceil(10000/depth)); % 确保可被交织深度整除
data = randi([0 1], total_bits, 1);
% 存储数据
handles.data.original = data;
handles.data.total_bits = total_bits;
% 显示结果
axes(handles.axes(1));
stem(data(1:min(40, length(data))), 'filled');
title(['原始二进制信号 (' num2str(length(data)) ' bits)']);
ylim([-0.2 1.2]); xlabel('比特序号'); ylabel('幅度');
% 更新状态
set(handles.status_text, 'String', '原始信号已生成');
guidata(hObject, handles);
end
%% 编码回调函数
function encode_data(hObject, ~)
handles = guidata(hObject);
% 检查是否有原始数据
if ~isfield(handles.data, 'original')
errordlg('请先生成原始信号!', '步骤错误');
return;
end
% 更新状态
set(handles.status_text, 'String', '正在进行编码...');
drawnow;
% 获取参数
coding_idx = get(handles.coding_type, 'Value'); % 编码类型索引
data = handles.data.original;
% 编码
if coding_idx == 1 % 卷积编码
trellis = poly2trellis(7, [171 133]);
coded_data = convenc(data, trellis);
title_str = '卷积编码后';
else % 无编码
coded_data = data;
title_str = '未编码数据';
end
% 存储数据
handles.data.coded = coded_data;
% 显示结果
axes(handles.axes(2));
stem(coded_data(1:min(80, length(coded_data))), 'filled');
title([title_str ' (' num2str(length(coded_data)) ' bits)']);
ylim([-0.2 1.2]); xlabel('比特序号'); ylabel('幅度');
% 更新状态
set(handles.status_text, 'String', '编码已完成');
guidata(hObject, handles);
end
%% 交织回调函数
function interleave_data(hObject, ~)
handles = guidata(hObject);
% 检查是否有编码数据
if ~isfield(handles.data, 'coded')
errordlg('请先进行编码!', '步骤错误');
return;
end
% 更新状态
set(handles.status_text, 'String', '正在进行交织...');
drawnow;
% 获取参数
depth = str2double(get(handles.intlvr_depth, 'String')); % 交织深度
coded_data = handles.data.coded;
% 交织
rows = depth;
cols = ceil(length(coded_data)/rows);
% 补零使数据完整
padded_data = [coded_data; zeros(rows*cols - length(coded_data), 1)];
interleaved = reshape(reshape(padded_data, rows, cols)', [], 1);
% 存储数据
handles.data.interleaved = interleaved;
% 显示结果
axes(handles.axes(3));
stem(interleaved(1:min(80, length(interleaved))), 'filled');
title(['交织后 (' num2str(depth) '行交织器)']);
ylim([-0.2 1.2]); xlabel('比特序号'); ylabel('幅度');
% 更新状态
set(handles.status_text, 'String', '交织已完成');
guidata(hObject, handles);
end
%% 调制回调函数
function modulate_data(hObject, ~)
handles = guidata(hObject);
% 检查是否有交织数据
if ~isfield(handles.data, 'interleaved')
errordlg('请先进行交织!', '步骤错误');
return;
end
% 更新状态
set(handles.status_text, 'String', '正在进行调制...');
drawnow;
% 获取参数
mod_idx = get(handles.mod_type, 'Value'); % 调制类型索引
N = str2double(get(handles.num_sc, 'String')); % 子载波数量
interleaved = handles.data.interleaved;
% 调制参数
mod_names = {'QPSK', '16QAM', 'BPSK'};
mod_orders = [4, 16, 2];
mod_str = mod_names{mod_idx};
M = mod_orders(mod_idx);
bits_per_symbol = log2(M);
% 串并转换
symbols_per_carrier = floor(length(interleaved)/(bits_per_symbol * N));
total_bits_needed = symbols_per_carrier * bits_per_symbol * N;
% 处理多余数据
if length(interleaved) > total_bits_needed
interleaved = interleaved(1:total_bits_needed);
end
% 重塑为并行流
parallel_data = reshape(interleaved, bits_per_symbol, []);
parallel_data = parallel_data';
% 调制
symbol_input = bi2de(reshape(parallel_data, [], bits_per_symbol), 'left-msb');
switch mod_idx
case 1 % QPSK
mod_data = pskmod(symbol_input, M, pi/4, 'gray');
case 2 % 16QAM
mod_data = qammod(symbol_input, M, 'gray', 'UnitAveragePower', true);
case 3 % BPSK
mod_data = pskmod(symbol_input, M, pi/2, 'gray');
end
% 重塑为OFDM帧
mod_data = reshape(mod_data, [], N);
% 存储数据
handles.data.modulated = mod_data;
handles.data.M = M;
handles.data.bits_per_symbol = bits_per_symbol;
handles.data.symbols_per_carrier = symbols_per_carrier;
% 显示结果
axes(handles.axes(4));
scatter(real(mod_data(:)), imag(mod_data(:)), 25, 'filled');
title([mod_str ' 调制星座图']);
axis square; grid on; xlabel('同相分量'); ylabel('正交分量');
% 更新状态
set(handles.status_text, 'String', '调制已完成');
guidata(hObject, handles);
end
%% IFFT变换回调函数
function ifft_transform(hObject, ~)
handles = guidata(hObject);
% 检查是否有调制数据
if ~isfield(handles.data, 'modulated')
errordlg('请先进行调制!', '步骤错误');
return;
end
% 更新状态
set(handles.status_text, 'String', '正在进行IFFT变换...');
drawnow;
% 获取参数
N = str2double(get(handles.num_sc, 'String')); % 子载波数量
mod_data = handles.data.modulated;
% IFFT变换
ofdm_symbols = ifft(mod_data, N, 2);
% 存储数据
handles.data.ofdm_symbols = ofdm_symbols;
% 显示结果
axes(handles.axes(5));
plot(abs(ofdm_symbols(1, :)));
title('OFDM符号 (时域)');
xlabel('样点序号'); ylabel('幅度');
% 更新状态
set(handles.status_text, 'String', 'IFFT变换已完成');
guidata(hObject, handles);
end
%% 加循环前缀回调函数
function add_cp(hObject, ~)
handles = guidata(hObject);
% 检查是否有OFDM符号数据
if ~isfield(handles.data, 'ofdm_symbols')
errordlg('请先进行IFFT变换!', '步骤错误');
return;
end
% 更新状态
set(handles.status_text, 'String', '正在添加循环前缀...');
drawnow;
% 获取参数
cp_length = str2double(get(handles.cp_len, 'String')); % 循环前缀长度
ofdm_symbols = handles.data.ofdm_symbols;
% 加循环前缀
cp = ofdm_symbols(:, end-cp_length+1:end);
ofdm_cp = [cp, ofdm_symbols];
% 计算频谱
Fs = 1e6; % 采样率1MHz
[pxx_base, f_base] = pwelch(ofdm_symbols(:), 512, 256, 512, Fs, 'centered');
[pxx_cp, f_cp] = pwelch(ofdm_cp(:), 512, 256, 512, Fs, 'centered');
% 存储数据
handles.data.ofdm_cp = ofdm_cp;
handles.data.Fs = Fs;
% 显示结果
axes(handles.axes(6));
plot(f_base/1e6, 10*log10(pxx_base), 'b'); hold on;
plot(f_cp/1e6, 10*log10(pxx_cp), 'r');
title('加CP前后频谱对比');
xlabel('频率 (MHz)'); ylabel('功率 (dB)');
legend('无CP', '有CP');
grid on; hold off;
% 更新状态
set(handles.status_text, 'String', '循环前缀已添加');
guidata(hObject, handles);
end
%% 上变频回调函数
function upconvert(hObject, ~)
handles = guidata(hObject);
% 检查是否有加CP后的数据
if ~isfield(handles.data, 'ofdm_cp')
errordlg('请先添加循环前缀!', '步骤错误');
return;
end
% 更新状态
set(handles.status_text, 'String', '正在进行上变频...');
drawnow;
% 获取参数
Fs = handles.data.Fs;
ofdm_cp = handles.data.ofdm_cp;
% 上变频
carrier_freq = 2e9; % 2GHz载波
t = (0:length(ofdm_cp(:))-1)'/Fs;
rf_signal = real(ofdm_cp(:)) .* cos(2*pi*carrier_freq*t) - ...
imag(ofdm_cp(:)) .* sin(2*pi*carrier_freq*t);
% 存储数据
handles.data.rf_signal = rf_signal;
handles.data.carrier_freq = carrier_freq;
% 显示频谱
axes(handles.axes(7));
[pxx_rf, f_rf] = pwelch(rf_signal, 1024, 512, 1024, Fs, 'centered');
plot(f_rf/1e6, 10*log10(pxx_rf));
title('天线输出频谱');
xlabel('频率 (MHz)'); ylabel('功率谱密度 (dB/Hz)');
grid on;
% 更新状态
set(handles.status_text, 'String', '上变频已完成');
guidata(hObject, handles);
end
%% 信道传输回调函数
function channel_transmission(hObject, ~)
handles = guidata(hObject);
% 检查是否有RF信号
if ~isfield(handles.data, 'rf_signal')
errordlg('请先进行上变频!', '步骤错误');
return;
end
% 更新状态
set(handles.status_text, 'String', '正在模拟信道传输...');
drawnow;
% 获取参数
snr = str2double(get(handles.snr, 'String')); % SNR
rf_signal = handles.data.rf_signal;
% 信道传输 (添加AWGN噪声)
rx_signal = awgn(rf_signal, snr, 'measured');
% 存储数据
handles.data.rx_signal = rx_signal;
% 更新状态
set(handles.status_text, 'String', '信道传输已完成');
guidata(hObject, handles);
end
%% 接收处理回调函数
function receive_process(hObject, ~)
handles = guidata(hObject);
% 检查是否有接收信号
if ~isfield(handles.data, 'rx_signal')
errordlg('请先进行信道传输!', '步骤错误');
return;
end
% 更新状态
set(handles.status_text, 'String', '正在进行接收处理...');
drawnow;
% 获取参数
Fs = handles.data.Fs;
carrier_freq = handles.data.carrier_freq;
rx_signal = handles.data.rx_signal;
% 接收处理
t = (0:length(rx_signal)-1)'/Fs;
I = rx_signal .* cos(2*pi*carrier_freq*t) * 2;
Q = -rx_signal .* sin(2*pi*carrier_freq*t) * 2;
baseband = complex(I, Q);
% 低通滤波
cutoff = Fs/2.5;
[b, a] = butter(6, cutoff/(Fs/2));
filtered = filtfilt(b, a, baseband);
% 存储数据
handles.data.filtered = filtered;
% 显示接收端星座图
axes(handles.axes(8));
mod_data = handles.data.modulated;
scatter(real(filtered(1:length(mod_data(:)))), ...
imag(filtered(1:length(mod_data(:)))), 25, 'filled');
title('接收端星座图 (含噪声)');
axis square; grid on; xlabel('同相分量'); ylabel('正交分量');
% 更新状态
set(handles.status_text, 'String', '接收处理已完成');
guidata(hObject, handles);
end
%% 解调回调函数
function demodulate_data(hObject, ~)
handles = guidata(hObject);
% 检查是否有接收数据
if ~isfield(handles.data, 'filtered')
errordlg('请先进行接收处理!', '步骤错误');
return;
end
% 更新状态
set(handles.status_text, 'String', '正在进行解调...');
drawnow;
% 获取参数
mod_idx = get(handles.mod_type, 'Value'); % 调制类型索引
M = handles.data.M;
filtered = handles.data.filtered;
mod_data = handles.data.modulated;
% 解调
switch mod_idx
case 1 % QPSK
demod = pskdemod(filtered(1:length(mod_data(:))), M, pi/4, 'gray');
case 2 % 16QAM
demod = qamdemod(filtered(1:length(mod_data(:))), M, 'gray', 'UnitAveragePower', true);
case 3 % BPSK
demod = pskdemod(filtered(1:length(mod_data(:))), M, pi/2, 'gray');
end
% 符号到比特
bits_per_symbol = handles.data.bits_per_symbol;
rx_bits = de2bi(demod, bits_per_symbol, 'left-msb');
rx_bits = rx_bits(:);
% 存储数据
handles.data.demod_bits = rx_bits;
% 更新状态
set(handles.status_text, 'String', '解调已完成');
guidata(hObject, handles);
end
%% 解交织回调函数
function deinterleave_data(hObject, ~)
handles = guidata(hObject);
% 检查是否有解调数据
if ~isfield(handles.data, 'demod_bits')
errordlg('请先进行解调!', '步骤错误');
return;
end
% 更新状态
set(handles.status_text, 'String', '正在进行解交织...');
drawnow;
% 获取参数
depth = str2double(get(handles.intlvr_depth, 'String')); % 交织深度
rx_bits = handles.data.demod_bits;
% 解交织
rows = depth;
cols = ceil(length(rx_bits)/rows);
padded_rx = [rx_bits; zeros(rows*cols - length(rx_bits), 1)];
deinterleaved = reshape(reshape(padded_rx, cols, rows)', [], 1);
% 存储数据
handles.data.deinterleaved = deinterleaved;
% 更新状态
set(handles.status_text, 'String', '解交织已完成');
guidata(hObject, handles);
end
%% 解码回调函数
function decode_data(hObject, ~)
handles = guidata(hObject);
% 检查是否有解交织数据
if ~isfield(handles.data, 'deinterleaved')
errordlg('请先进行解交织!', '步骤错误');
return;
end
% 更新状态
set(handles.status_text, 'String', '正在进行解码...');
drawnow;
% 获取参数
coding_idx = get(handles.coding_type, 'Value'); % 编码类型索引
deinterleaved = handles.data.deinterleaved;
% 解码
if coding_idx == 1 % 卷积编码
trellis = poly2trellis(7, [171 133]);
decoded = vitdec(deinterleaved, trellis, 7, 'trunc', 'hard');
else % 无编码
decoded = deinterleaved;
end
% 存储数据
handles.data.decoded = decoded;
% 更新状态
set(handles.status_text, 'String', '解码已完成');
guidata(hObject, handles);
end
%% 误码率分析回调函数
function ber_analysis(hObject, ~)
handles = guidata(hObject);
% 检查是否有原始数据和解码数据
if ~isfield(handles.data, 'original') || ~isfield(handles.data, 'decoded')
errordlg('请先完成所有步骤!', '步骤错误');
return;
end
% 更新状态
set(handles.status_text, 'String', '正在进行误码率分析...');
drawnow;
% 获取数据
original = handles.data.original;
decoded = handles.data.decoded;
% 确保长度匹配
len = min(length(original), length(decoded));
original = original(1:len);
decoded = decoded(1:len);
% 计算误码率
errors = sum(original ~= decoded);
ber = errors / len;
% 显示结果
axes(handles.axes(9));
bar(categorical({'BER'}), ber);
ylim([0 0.5]);
title(['误码率: ' num2str(ber*100) '% (' num2str(errors) '/' num2str(len) '错误)']);
ylabel('误码率');
% 显示原始和解码数据对比
figure('Name', '原始与解码数据对比', 'Position', [200 200 800 400]);
subplot(2,1,1);
stem(original(1:min(50, len)), 'filled');
title('原始数据');
subplot(2,1,2);
stem(decoded(1:min(50, len)), 'filled');
title('解码数据');
% 更新状态
set(handles.status_text, 'String', sprintf('误码率分析完成: BER = %.4f', ber));
guidata(hObject, handles);
end
把这段代码修改为低一点误码率的正确完整的代码
最新发布