关于sprintf整合%d出错的问题

本文介绍了一个使用sprintf函数时常见的问题,即当编译器无法识别%d类型的数据时可能导致的输出错误,并提供了一种通过类型强制转换来解决问题的方法。

有些编译器用到sprintf函数整合%d类型的数据时候可能会出错例如

错误的示例

char *p[80]=0;
memset(p,0,sizeof(p));
sprintf((char *)p,"AT+CIPSTART=\"TCP\",\"%d.%d.%d.%d\",%d\r\n",210,209,82,119,8080);
PrintString1(p);

输出的值为 : AT+CIPSTART=”TCP”,”-11567.21111.8080.0”,53

这是因为某些编译器不知道需要整合的数是什么类型导致的 所以需要加上强制转换类型才可以输出我们需要的答案
正确的方法

char *p[80]=0;
memset(p,0,sizeof(p));
sprintf((char *)p,"AT+CIPSTART=\"TCP\",\"%d.%d.%d.%d\",%d\r\n",(int)210,(int)209,(int)82,(int)119,(int)8080);
PrintString1(p);

此时输出的是 :AT+CIPSTART=”TCP”,”210.209.82.119”,8080

clc,clear tic; disp('正在读取数据...'); % 读取海运轨迹数据 - 修复变量名问题 opts = detectImportOptions('gj.xlsx'); % 确保变量类型和变量名数量一致 if numel(opts.VariableTypes) ~= 10 error('轨迹数据文件列数不是10列,请检查文件格式'); end opts.VariableTypes = {'double', 'char', 'char', 'char', 'double', 'double', 'double', 'char', 'char', 'double'}; trackData = readtable('gj.xlsx', opts); % 直接设置变量名(避免使用Properties.VariableNames结构) trackData.Properties.VariableNames = {'timestamp', 'vessel_id', 'origin_port', 'destination_port', ... 'latitude', 'longitude', 'speed', 'status', 'anomaly_type', 'fuel_consumption'}; % 读取港口数据 portData = readtable('zc.xlsx'); portData.Properties.VariableNames = {'port_name', 'port_code', 'country', 'port_latitude', 'port_longitude'}; % 读取船舶信息 vesselData = readtable('yy.xlsx'); vesselData.Properties.VariableNames = {'vessel_id', 'vessel_type', 'build_year', 'dwt', 'max_speed'}; disp(['数据读取完成! 耗时: ', num2str(toc), ' 秒']); fprintf('轨迹数据记录数: %d\n', height(trackData)); fprintf('港口数据记录数: %d\n', height(portData)); fprintf('船舶信息记录数: %d\n', height(vesselData)); %% 步骤2:数据清洗 tic; disp('正在进行数据清洗...'); % 转换时间戳为日期时间格式(Excel序列日期 -> MATLAB日期时间) trackData.timestamp = datetime(trackData.timestamp, 'ConvertFrom', 'excel'); % 处理异常值 % 速度在0-50节之间,经纬度在有效范围内 validSpeed = trackData.speed >= 0 & trackData.speed <= 50; validLatitude = trackData.latitude >= -90 & trackData.latitude <= 90; validLongitude = trackData.longitude >= -180 & trackData.longitude <= 180; validCoords = validLatitude & validLongitude; trackData = trackData(validSpeed & validCoords, :); % 填充缺失的异常类型 missingAnomaly = cellfun(@isempty, trackData.anomaly_type); trackData.anomaly_type(missingAnomaly) = {'NONE'}; % 标准化港口名称 portData.port_name = upper(portData.port_name); trackData.origin_port = upper(trackData.origin_port); trackData.destination_port = upper(trackData.destination_port); % 船舶ID格式标准化 trackData.vessel_id = regexprep(trackData.vessel_id, '\s+', ''); vesselData.vessel_id = regexprep(vesselData.vessel_id, '\s+', ''); disp(['数据清洗完成! 耗时: ', num2str(toc), ' 秒']); fprintf('清洗后轨迹数据记录数: %d\n', height(trackData)); %% 步骤3:数据匹配 tic; disp('正在进行数据匹配...'); % 创建港口名称到港口代码的映射 portCodeMap = containers.Map(portData.port_name, portData.port_code); portCountryMap = containers.Map(portData.port_name, portData.country); % 添加港口信息到轨迹数据 trackData.origin_port_code = cell(height(trackData), 1); trackData.destination_port_code = cell(height(trackData), 1); trackData.origin_country = cell(height(trackData), 1); trackData.destination_country = cell(height(trackData), 1); for i = 1:height(trackData) originPort = trackData.origin_port{i}; if isKey(portCodeMap, originPort) trackData.origin_port_code{i} = portCodeMap(originPort); trackData.origin_country{i} = portCountryMap(originPort); else trackData.origin_port_code{i} = 'UNKNOWN'; trackData.origin_country{i} = 'UNKNOWN'; end destPort = trackData.destination_port{i}; if isKey(portCodeMap, destPort) trackData.destination_port_code{i} = portCodeMap(destPort); trackData.destination_country{i} = portCountryMap(destPort); else trackData.destination_port_code{i} = 'UNKNOWN'; trackData.destination_country{i} = 'UNKNOWN'; end end % 船舶信息匹配 - 使用更高效的向量化方法 [isMember, idx] = ismember(trackData.vessel_id, vesselData.vessel_id); trackData.vessel_type = repmat({''}, height(trackData), 1); trackData.build_year = nan(height(trackData), 1); trackData.dwt = nan(height(trackData), 1); trackData.max_speed = nan(height(trackData), 1); trackData.vessel_type(isMember) = vesselData.vessel_type(idx(isMember)); trackData.build_year(isMember) = vesselData.build_year(idx(isMember)); trackData.dwt(isMember) = vesselData.dwt(idx(isMember)); trackData.max_speed(isMember) = vesselData.max_speed(idx(isMember)); disp(['数据匹配完成! 耗时: ', num2str(toc), ' 秒']); %% 步骤4:数据质量检查 tic; disp('正在进行数据质量检查...'); % 检查未匹配的港口 missingOrigin = strcmp(trackData.origin_port_code, 'UNKNOWN'); missingDest = strcmp(trackData.destination_port_code, 'UNKNOWN'); fprintf('未匹配的起始港: %.2f%%\n', 100*nnz(missingOrigin)/height(trackData)); fprintf('未匹配的目的港: %.2f%%\n', 100*nnz(missingDest)/height(trackData)); % 检查未匹配的船舶信息 missingVessel = isnan(trackData.build_year); fprintf('未匹配的船舶信息: %.2f%%\n', 100*nnz(missingVessel)/height(trackData)); % 创建异常报告 anomalyReport = table(); if nnz(missingOrigin) > 0 tempReport = trackData(missingOrigin, {'origin_port', 'timestamp', 'vessel_id'}); tempReport.Properties.VariableNames = {'Port', 'Timestamp', 'Vessel_ID'}; tempReport.Issue = repmat({'Missing Origin Port'}, height(tempReport), 1); anomalyReport = [anomalyReport; tempReport]; end if nnz(missingDest) > 0 tempReport = trackData(missingDest, {'destination_port', 'timestamp', 'vessel_id'}); tempReport.Properties.VariableNames = {'Port', 'Timestamp', 'Vessel_ID'}; tempReport.Issue = repmat({'Missing Destination Port'}, height(tempReport), 1); anomalyReport = [anomalyReport; tempReport]; end if nnz(missingVessel) > 0 tempReport = trackData(missingVessel, {'vessel_id', 'timestamp'}); tempReport.Properties.VariableNames = {'Vessel_ID', 'Timestamp'}; tempReport.Issue = repmat({'Missing Vessel Info'}, height(tempReport), 1); anomalyReport = [anomalyReport; tempReport]; end % 保存异常报告 if ~isempty(anomalyReport) writetable(anomalyReport, '数据匹配异常报告.xlsx'); disp('已保存异常报告到: 数据匹配异常报告.xlsx'); end % 移除完全未匹配的记录 validRecords = ~(strcmp(trackData.origin_port_code, 'UNKNOWN') | ... strcmp(trackData.destination_port_code, 'UNKNOWN') | ... isnan(trackData.build_year)); trackData = trackData(validRecords, :); fprintf('有效记录数: %d (已移除 %d 条无效记录)\n', ... height(trackData), nnz(~validRecords)); disp(['数据质量检查完成! 耗时: ', num2str(toc), ' 秒']); %% 步骤5:计算衍生指标并保存结果 tic; disp('正在计算衍生指标...'); % 计算速度比(当前速度/船舶最大速度) trackData.speed_ratio = trackData.speed ./ trackData.max_speed; % 标记异常速度(速度比 < 0.2 或 > 1.1) trackData.speed_anomaly = trackData.speed_ratio < 0.2 | trackData.speed_ratio > 1.1; % 添加航程标记 (优化性能) [~, ~, journeyGroup] = unique(strcat(trackData.vessel_id, '_', trackData.origin_port, '_', trackData.destination_port)); trackData.journey_id = arrayfun(@(x) sprintf('JID_%d', x), journeyGroup, 'UniformOutput', false); disp(['衍生指标计算完成! 耗时: ', num2str(toc), ' 秒']); %% 步骤6:保存最终结果 tic; disp('正在保存结果...'); % 重新组织列顺序 finalData = trackData(:, { 'timestamp', 'vessel_id', 'vessel_type', 'build_year', 'dwt', 'journey_id', ... 'origin_port', 'origin_port_code', 'origin_country', ... 'destination_port', 'destination_port_code', 'destination_country', ... 'latitude', 'longitude', 'speed', 'max_speed', 'speed_ratio', ... 'fuel_consumption', 'status', 'anomaly_type', 'speed_anomaly' }); % 保存为MAT文件(完整数据) save('combinedMarineData.mat', 'finalData', '-v7.3'); % 保存为Parquet文件(高效列式存储) parquetwrite('combinedMarineData.parquet', finalData); % 保存为Excel(适合分析) writetable(finalData, 'combinedMarineData.xlsx'); % 保存港口和船舶数据 save('portData.mat', 'portData'); save('vesselData.mat', 'vesselData'); disp(['结果保存完成! 耗时: ', num2str(toc), ' 秒']); disp(['总处理时间: ', num2str(toc), ' 秒']); fprintf('最终数据集大小: %d 行 x %d 列\n', size(finalData, 1), size(finalData, 2)); %% 步骤7:验证结果 disp('数据整合报告:'); disp('========================================'); fprintf('轨迹记录: %d 条\n', height(finalData)); fprintf('唯一船舶: %d 艘\n', numel(unique(finalData.vessel_id))); fprintf('唯一航程: %d 个\n', numel(unique(finalData.journey_id))); fprintf('覆盖港口: %d 个\n', numel(unique([finalData.origin_port; finalData.destination_port]))); fprintf('时间范围: %s 至 %s\n', ... datestr(min(finalData.timestamp), 'yyyy-mm-dd'), ... datestr(max(finalData.timestamp), 'yyyy-mm-dd')); fprintf('异常速度记录: %d 条 (%.2f%%)\n', ... nnz(finalData.speed_anomaly), 100*nnz(finalData.speed_anomaly)/height(finalData)); disp('========================================'); (>> maritime_data_cleaning 正在读取数据... 错误使用 maritime_data_cleaning 对于表中的每个变量,VariableNames 属性必须包含一个名称。)
最新发布
08-25
function timbre_transfer % 创建主界面 fig = figure('Name', '高级音色转换系统 v3.2', 'Position', [50, 50, 1200, 900], ... 'NumberTitle', 'off', 'MenuBar', 'none', 'Resize', 'on', ... 'CloseRequestFcn', @close_gui, 'Color', [0.94, 0.94, 0.94]); % 全局变量 fs = 44100; % 默认采样率 source_audio = []; % 源音频(提供音色) target_audio = []; % 目标音频(提供内容) converted_audio = []; % 转换后的音频 processing = false; % 处理状态标志 conversion_complete = false; % 转换完成标志 % STFT参数 stft_params.win_len = 2048; % 窗长 stft_params.overlap = 1536; % 重叠点数 (75%) stft_params.nfft = 2048; % FFT点数 stft_params.window = hamming(stft_params.win_len, 'periodic'); % 汉明窗 stft_params.lifter_order = 30; % 包络阶数 stft_params.phase_iter = 5; % 相位迭代次数 stft_params.fs = fs; % 采样率参数 stft_params.hop_size = stft_params.win_len - stft_params.overlap; % 跳跃长度 % 计算合成窗 (确保完美重建) stft_params.win_synthesis = stft_params.window / sum(stft_params.window.^2) * stft_params.hop_size; % === 创建控件 === % 顶部控制面板 control_panel = uipanel('Title', '音频控制', 'Position', [0.02, 0.92, 0.96, 0.07], ... 'BackgroundColor', [0.9, 0.95, 1]); uicontrol('Parent', control_panel, 'Style', 'pushbutton', 'String', '导入源音频(音色)',... 'Position', [20, 10, 150, 30], 'Callback', @load_source, ... 'FontSize', 10, 'FontWeight', 'bold', 'BackgroundColor', [0.7, 0.9, 1]); uicontrol('Parent', control_panel, 'Style', 'pushbutton', 'String', '导入目标音频(内容)',... 'Position', [190, 10, 150, 30], 'Callback', @load_target, ... 'FontSize', 10, 'FontWeight', 'bold', 'BackgroundColor', [0.7, 0.9, 1]); uicontrol('Parent', control_panel, 'Style', 'pushbutton', 'String', '执行音色转换',... 'Position', [360, 10, 150, 30], 'Callback', @transfer_timbre, ... 'FontSize', 10, 'FontWeight', 'bold', 'BackgroundColor', [0.8, 1, 0.8]); uicontrol('Parent', control_panel, 'Style', 'pushbutton', 'String', '播放目标音频',... 'Position', [530, 10, 120, 30], 'Callback', @(src,evt) play_audio(target_audio, fs), ... 'FontSize', 10, 'BackgroundColor', [1, 0.95, 0.8]); uicontrol('Parent', control_panel, 'Style', 'pushbutton', 'String', '播放转换音频',... 'Position', [670, 10, 120, 30], 'Callback', @(src,evt) play_audio(converted_audio, fs), ... 'FontSize', 10, 'BackgroundColor', [1, 0.95, 0.8]); uicontrol('Parent', control_panel, 'Style', 'pushbutton', 'String', '保存转换音频',... 'Position', [810, 10, 120, 30], 'Callback', @save_audio, ... 'FontSize', 10, 'BackgroundColor', [0.9, 1, 0.8]); % 参数控制面板 param_panel = uipanel('Title', 'STFT参数设置', 'Position', [0.02, 0.82, 0.96, 0.09], ... 'BackgroundColor', [0.95, 0.97, 1], 'FontWeight', 'bold'); uicontrol('Parent', param_panel, 'Style', 'text', 'String', '窗长:',... 'Position', [20, 40, 50, 20], 'HorizontalAlignment', 'left',... 'BackgroundColor', [0.95, 0.97, 1], 'FontWeight', 'bold'); win_len_edit = uicontrol('Parent', param_panel, 'Style', 'edit',... 'String', num2str(stft_params.win_len),... 'Position', [80, 40, 80, 25], 'Callback', @update_params, ... 'BackgroundColor', [1, 1, 1]); uicontrol('Parent', param_panel, 'Style', 'text', 'String', '重叠率(%):',... 'Position', [180, 40, 70, 20], 'HorizontalAlignment', 'left',... 'BackgroundColor', [0.95, 0.97, 1], 'FontWeight', 'bold'); overlap_edit = uicontrol('Parent', param_panel, 'Style', 'edit',... 'String', '75',... 'Position', [260, 40, 80, 25], 'Callback', @update_params, ... 'BackgroundColor', [1, 1, 1]); uicontrol('Parent', param_panel, 'Style', 'text', 'String', 'FFT点数:',... 'Position', [360, 40, 60, 20], 'HorizontalAlignment', 'left',... 'BackgroundColor', [0.95, 0.97, 1], 'FontWeight', 'bold'); nfft_edit = uicontrol('Parent', param_panel, 'Style', 'edit',... 'String', num2str(stft_params.nfft),... 'Position', [430, 40, 80, 25], 'Callback', @update_params, ... 'BackgroundColor', [1, 1, 1]); uicontrol('Parent', param_panel, 'Style', 'text', 'String', '包络阶数:',... 'Position', [530, 40, 60, 20], 'HorizontalAlignment', 'left',... 'BackgroundColor', [0.95, 0.97, 1], 'FontWeight', 'bold'); lifter_edit = uicontrol('Parent', param_panel, 'Style', 'edit',... 'String', num2str(stft_params.lifter_order),... 'Position', [600, 40, 80, 25], 'Callback', @update_params, ... 'BackgroundColor', [1, 1, 1]); uicontrol('Parent', param_panel, 'Style', 'text', 'String', '相位迭代:',... 'Position', [700, 40, 60, 20], 'HorizontalAlignment', 'left',... 'BackgroundColor', [0.95, 0.97, 1], 'FontWeight', 'bold'); iter_edit = uicontrol('Parent', param_panel, 'Style', 'edit',... 'String', num2str(stft_params.phase_iter),... 'Position', [770, 40, 80, 25], 'Callback', @update_params, ... 'BackgroundColor', [1, 1, 1]); % 波形显示区域 - 使用选项卡 tabgp = uitabgroup(fig, 'Position', [0.02, 0.02, 0.96, 0.35]); tab1 = uitab(tabgp, 'Title', '目标音频'); tab2 = uitab(tabgp, 'Title', '转换后音频'); tab3 = uitab(tabgp, 'Title', '源音频'); ax1 = axes('Parent', tab1, 'Position', [0.07, 0.15, 0.9, 0.75]); title(ax1, '目标音频波形'); xlabel(ax1, '时间 (s)'); ylabel(ax1, '幅度'); grid(ax1, 'on'); ax2 = axes('Parent', tab2, 'Position', [0.07, 0.15, 0.9, 0.75]); title(ax2, '转换后音频波形'); xlabel(ax2, '时间 (s)'); ylabel(ax2, '幅度'); grid(ax2, 'on'); ax3 = axes('Parent', tab3, 'Position', [0.07, 0.15, 0.9, 0.75]); title(ax3, '源音频波形'); xlabel(ax3, '时间 (s)'); ylabel(ax3, '幅度'); grid(ax3, 'on'); % 频谱显示区域(只保留三个频谱图) spec_panel = uipanel('Title', '频谱分析', 'Position', [0.02, 0.38, 0.96, 0.43], ... 'BackgroundColor', [0.98, 0.98, 0.98], 'FontWeight', 'bold'); % 增大频谱图尺寸(垂直方向) ax4 = axes('Parent', spec_panel, 'Position', [0.03, 0.1, 0.3, 0.8]); % 高度增加到80% title(ax4, '源音频频谱'); ax5 = axes('Parent', spec_panel, 'Position', [0.36, 0.1, 0.3, 0.8]); % 高度增加到80% title(ax5, '目标音频频谱'); ax6 = axes('Parent', spec_panel, 'Position', [0.69, 0.1, 0.3, 0.8]); % 高度增加到80% title(ax6, '转换后频谱'); % 状态文本 status_text = uicontrol('Style', 'text', 'Position', [50, 5, 900, 30],... 'String', '就绪', 'HorizontalAlignment', 'left',... 'FontSize', 10, 'FontWeight', 'bold', 'BackgroundColor', [1, 1, 1]); % 进度条 progress_ax = axes('Position', [0.1, 0.97, 0.8, 0.02],... 'XLim', [0, 1], 'YLim', [0, 1], 'Box', 'on', 'Color', [0.9, 0.9, 0.9]); progress_bar = patch(progress_ax, [0 0 0 0], [0 0 1 1], [0.2, 0.6, 1]); axis(progress_ax, 'off'); progress_text = uicontrol('Style', 'text', 'Position', [500, 970, 200, 20],... 'String', '', 'HorizontalAlignment', 'center',... 'FontSize', 10, 'FontWeight', 'bold', 'BackgroundColor', [1, 1, 1]); % 诊断信息面板 diag_panel = uipanel('Title', '处理日志', 'Position', [0.02, 0.02, 0.96, 0.35], ... 'BackgroundColor', [0.95, 0.95, 0.95], 'Visible', 'off'); diag_text = uicontrol('Parent', diag_panel, 'Style', 'listbox', ... 'Position', [10, 10, 1140, 250], 'String', {'系统已初始化'}, ... 'HorizontalAlignment', 'left', 'FontSize', 9, ... 'BackgroundColor', [1, 1, 1], 'Max', 100, 'Min', 0); % 添加显示/隐藏日志按钮 uicontrol('Style', 'pushbutton', 'String', '显示日志',... 'Position', [1020, 920, 100, 30], 'Callback', @toggle_log, ... 'FontSize', 9, 'BackgroundColor', [0.9, 0.95, 1]); % === 回调函数 === % 更新参数回调 function update_params(~, ~) try % 获取新参数值 new_win_len = str2double(get(win_len_edit, 'String')); overlap_percent = str2double(get(overlap_edit, 'String')); new_nfft = str2double(get(nfft_edit, 'String')); lifter_order = str2double(get(lifter_edit, 'String')); phase_iter = str2double(get(iter_edit, 'String')); % 验证参数 if isnan(new_win_len) || new_win_len <= 0 || mod(new_win_len, 1) ~= 0 error('窗长必须是正整数'); end if isnan(overlap_percent) || overlap_percent < 0 || overlap_percent > 100 error('重叠率必须是0-100之间的数字'); end if isnan(new_nfft) || new_nfft <= 0 || mod(new_nfft, 1) ~= 0 error('FFT点数必须是正整数'); end if isnan(lifter_order) || lifter_order <= 0 || mod(lifter_order, 1) ~= 0 error('包络阶数必须是正整数'); end if isnan(phase_iter) || phase_iter <= 0 || mod(phase_iter, 1) ~= 0 error('相位迭代次数必须是正整数'); end % 更新参数 stft_params.win_len = new_win_len; stft_params.overlap = round(overlap_percent/100 * new_win_len); stft_params.nfft = new_nfft; stft_params.window = hamming(new_win_len, 'periodic'); stft_params.lifter_order = lifter_order; stft_params.phase_iter = phase_iter; update_diag(sprintf('参数更新: 窗长=%d, 重叠=%d(%.0f%%), FFT=%d', ... new_win_len, stft_params.overlap, overlap_percent, new_nfft)); catch e errordlg(['参数错误: ', e.message], '输入错误'); update_diag(['参数错误: ', e.message], true); end end % 更新诊断信息 function update_diag(msg, force) if nargin < 2, force = false; end if ~conversion_complete || force current = get(diag_text, 'String'); new_msg = sprintf('[%s] %s', datestr(now, 'HH:MM:SS'), msg); set(diag_text, 'String', [current; {new_msg}]); set(diag_text, 'Value', length(get(diag_text, 'String'))); end end % 切换日志显示 function toggle_log(~, ~) if strcmp(get(diag_panel, 'Visible'), 'on') set(diag_panel, 'Visible', 'off'); set(tabgp, 'Position', [0.02, 0.02, 0.96, 0.35]); else set(diag_panel, 'Visible', 'on'); set(tabgp, 'Position', [0.02, 0.38, 0.96, 0.35]); end end % 关闭GUI回调 function close_gui(~, ~) if processing choice = questdlg('处理正在进行中,确定要关闭吗?', '确认关闭', '是', '否', '否'); if strcmp(choice, '否') return; end end delete(fig); end % 导入源音频 function load_source(~, ~) if processing, return; end [file, path] = uigetfile({'*.wav;*.mp3;*.ogg', '音频文件 (*.wav,*.mp3,*.ogg)'}); if isequal(file, 0), return; end try [audio, fs_in] = audioread(fullfile(path, file)); update_diag(['加载源音频: ', file, ' (', num2str(fs_in), 'Hz)']); set(status_text, 'String', ['正在处理源音频: ', file]); drawnow; % 转换为单声道并归一化 if size(audio, 2) > 1 source_audio = mean(audio, 2); update_diag('转换为单声道'); else source_audio = audio; end source_audio = source_audio / max(abs(source_audio)); update_diag('归一化完成'); % 更新采样率参数 stft_params.fs = fs; % 采样率处理 if fs == 0 fs = fs_in; elseif fs ~= fs_in update_diag(['重采样: ', num2str(fs_in), 'Hz -> ', num2str(fs), 'Hz']); source_audio = resample(source_audio, fs, fs_in); end % 显示波形和频谱 plot(ax3, (0:length(source_audio)-1)/fs, source_audio); title(ax3, ['源音频波形: ', file]); xlabel(ax3, '时间 (s)'); ylabel(ax3, '幅度'); grid(ax3, 'on'); % 显示频谱(不再显示包络) show_spectrum(ax4, source_audio, fs, stft_params, '源音频频谱'); set(status_text, 'String', ['已加载源音频: ', file, ' (', num2str(fs/1000), 'kHz)']); update_diag(['源音频长度: ', num2str(length(source_audio)/fs), '秒']); % 重置转换完成标志 conversion_complete = false; catch e errordlg(['加载源音频失败: ', e.message], '错误'); update_diag(['错误: ', e.message], true); end end % 导入目标音频 function load_target(~, ~) if processing, return; end [file, path] = uigetfile({'*.wav;*.mp3;*.ogg', '音频文件 (*.wav,*.mp3,*.ogg)'}); if isequal(file, 0), return; end try [audio, fs_in] = audioread(fullfile(path, file)); update_diag(['加载目标音频: ', file, ' (', num2str(fs_in), 'Hz)']); set(status_text, 'String', ['正在处理目标音频: ', file]); drawnow; % 转换为单声道并归一化 if size(audio, 2) > 1 target_audio = mean(audio, 2); update_diag('转换为单声道'); else target_audio = audio; end target_audio = target_audio / max(abs(target_audio)); update_diag('归一化完成'); % 更新采样率参数 stft_params.fs = fs; % 采样率处理 if fs == 0 fs = fs_in; elseif fs ~= fs_in update_diag(['重采样: ', num2str(fs_in), 'Hz -> ', num2str(fs), 'Hz']); target_audio = resample(target_audio, fs, fs_in); end % 显示波形和频谱 plot(ax1, (0:length(target_audio)-1)/fs, target_audio); title(ax1, ['目标音频波形: ', file]); xlabel(ax1, '时间 (s)'); ylabel(ax1, '幅度'); grid(ax1, 'on'); % 显示频谱(不再显示包络) show_spectrum(ax5, target_audio, fs, stft_params, '目标音频频谱'); set(status_text, 'String', ['已加载目标音频: ', file, ' (', num2str(fs/1000), 'kHz)']); update_diag(['目标音频长度: ', num2str(length(target_audio)/fs), '秒']); % 重置转换完成标志 conversion_complete = false; catch e errordlg(['加载目标音频失败: ', e.message], '错误'); update_diag(['错误: ', e.message], true); end end % 核心音色转换函数 function transfer_timbre(~, ~) if processing, return; end if isempty(source_audio) || isempty(target_audio) errordlg('请先导入源音频和目标音频!', '错误'); return; end % 设置处理状态 processing = true; conversion_complete = false; set(status_text, 'String', '开始音色转换...'); update_diag('=== 开始音色转换 ==='); drawnow; % 统一音频长度(以目标音频长度为基准) target_length = length(target_audio); source_length = length(source_audio); if source_length < target_length % 源音频较短,重复填充 num_repeat = ceil(target_length / source_length); extended_source = repmat(source_audio, num_repeat, 1); source_audio_adj = extended_source(1:target_length); update_diag('源音频已扩展以匹配目标长度'); elseif source_length > target_length % 源音频较长,截断 source_audio_adj = source_audio(1:target_length); update_diag('源音频已截断以匹配目标长度'); else source_audio_adj = source_audio; end % 确保长度兼容 target_audio_adj = target_audio(1:min(target_length, length(source_audio_adj))); source_audio_adj = source_audio_adj(1:min(target_length, length(source_audio_adj))); try % === 目标音频STFT === update_diag('对目标音频进行STFT...'); update_progress(0.1, '目标音频STFT'); [S_target, ~, ~] = optimized_stft(target_audio_adj, stft_params, @update_progress); mag_target = abs(S_target); phase_target = angle(S_target); update_diag(sprintf('目标音频STFT完成: %d帧', size(S_target,2))); % === 源音频STFT === update_diag('对源音频进行STFT...'); update_progress(0.3, '源音频STFT'); [S_source, ~, ~] = optimized_stft(source_audio_adj, stft_params, @update_progress); mag_source = abs(S_source); update_diag(sprintf('源音频STFT完成: %d帧', size(S_source,2))); % 确保频谱矩阵大小相同 if size(mag_target, 2) ~= size(mag_source, 2) min_frames = min(size(mag_target, 2), size(mag_source, 2)); mag_target = mag_target(:, 1:min_frames); mag_source = mag_source(:, 1:min_frames); phase_target = phase_target(:, 1:min_frames); update_diag(sprintf('调整频谱帧数: %d帧', min_frames)); end % === 改进的频谱转换算法 === update_diag('应用改进的音色转换算法...'); update_progress(0.65, '频谱转换'); % 1. 计算源音频的频谱包络 mag_source_env = spectral_envelope(mag_source, stft_params.lifter_order, stft_params.nfft); % 2. 计算目标音频的频谱包络 mag_target_env = spectral_envelope(mag_target, stft_params.lifter_order, stft_params.nfft); % 3. 计算源音频的频谱细节 mag_source_detail = mag_source ./ (mag_source_env + eps); % 4. 应用转换:目标包络 + 源细节 mag_new = mag_target_env .* mag_source_detail; % 5. 相位重建(Griffin-Lim相位重建) update_diag('相位重建...'); update_progress(0.80, '相位重建'); phase_new = phase_reconstruction(mag_new, phase_target, stft_params, stft_params.phase_iter); % === 重建音频 === update_diag('重建音频(ISTFT)...'); update_progress(0.90, 'ISTFT重建'); converted_audio = optimized_istft(mag_new, phase_new, stft_params, @update_progress); converted_audio = converted_audio / max(abs(converted_audio)); % 归一化 % 确保长度匹配 if length(converted_audio) > target_length converted_audio = converted_audio(1:target_length); elseif length(converted_audio) < target_length converted_audio = [converted_audio; zeros(target_length - length(converted_audio), 1)]; end % 显示结果 plot(ax2, (0:length(converted_audio)-1)/fs, converted_audio); title(ax2, '转换后音频波形'); xlabel(ax2, '时间 (s)'); ylabel(ax2, '幅度'); grid(ax2, 'on'); % 显示转换后频谱(不再显示包络) show_spectrum(ax6, converted_audio, fs, stft_params, '转换后频谱'); % 更新状态 update_progress(1.0, '转换完成'); set(status_text, 'String', '音色转换完成!'); update_diag('音色转换成功!', true); % 设置完成标志 conversion_complete = true; % 清理大内存变量 clear S_target S_source mag_target mag_source mag_new; catch e errordlg(['音色转换失败: ', e.message], '错误'); update_diag(['错误: ', e.message], true); set(progress_bar, 'FaceColor', [1, 0.3, 0.3]); set(progress_text, 'String', '处理失败'); end % 重置处理状态 processing = false; end function phase = phase_reconstruction(mag, phase_init, params, progress_callback) % 相位重建函数 - 使用参数指定的迭代次数 % 输入: % mag - 目标幅度谱 (单边谱) % phase_init - 初始相位谱 (单边谱) % params - 参数结构体 (包含 phase_iter 等参数) % progress_callback - 进度回调函数 % 输出: % phase - 重建后的相位谱 % === 参数提取 === nfft = params.nfft; griffin_lim_iters = params.phase_iter; % 使用参数中的迭代次数 [num_bins, num_frames] = size(mag); % === 初始化 === current_phase = phase_init; % 进度更新间隔 update_interval = max(1, floor(griffin_lim_iters/10)); % === Griffin-Lim 迭代相位重建 === for iter = 1:griffin_lim_iters % 1. 创建复数频谱 (单边转双边) S_complex = create_full_spectrum(mag .* exp(1i*current_phase), nfft); % 2. ISTFT重建时域信号 x_recon = optimized_istft(mag, current_phase, params, []); % 3. 对重建信号进行STFT [~, ~, ~, S_new] = optimized_stft(x_recon, params, []); % 4. 更新相位 current_phase = angle(S_new); % 5. 进度更新 if ~isempty(progress_callback) && mod(iter, update_interval) == 0 progress_callback(iter/griffin_lim_iters * 0.2, ... sprintf('相位重建: %d/%d', iter, griffin_lim_iters)); end end phase = current_phase; % === 辅助函数: 创建完整频谱 === function S_full = create_full_spectrum(S_half, nfft) % 从单边谱创建双边谱 num_bins = size(S_half, 1); S_full = zeros(nfft, size(S_half, 2)); if rem(nfft, 2) % 奇数点FFT S_full(1:num_bins, :) = S_half; S_full(num_bins+1:end, :) = conj(S_half(end:-1:2, :)); else % 偶数点FFT S_full(1:num_bins, :) = S_half; S_full(num_bins+1:end, :) = conj(S_half(end-1:-1:2, :)); end end end function env = spectral_envelope(mag, lifter_order, nfft) % 计算频谱包络 % 输入: % mag - 频谱幅度 (单边谱) % lifter_order - 包络阶数 % nfft - FFT点数 % 输出: % env - 频谱包络 [num_bins, num_frames] = size(mag); % === 1. 参数验证 === if lifter_order >= nfft/2 lifter_order = floor(nfft/2) - 1; warning('包络阶数过大,自动调整为 %d', lifter_order); end % === 2. 重建双边谱 === if rem(nfft, 2) % 奇数点FFT full_mag = zeros(nfft, num_frames); full_mag(1:num_bins, :) = mag; full_mag(num_bins+1:end, :) = conj(mag(end:-1:2, :)); else % 偶数点FFT full_mag = zeros(nfft, num_frames); full_mag(1:num_bins, :) = mag; full_mag(num_bins+1:end, :) = conj(mag(end-1:-1:2, :)); end % === 3. 计算倒谱 === % 对数幅度谱 log_mag = log(full_mag + eps); % 避免log(0) % 倒谱 = ifft(对数幅度谱) cepstrum = ifft(log_mag, nfft, 'symmetric'); % === 4. 创建提升器窗口 === lifter = zeros(nfft, 1); % 保留低频部分 lifter(1:lifter_order+1) = 1; % 保留对称的高频部分 if nfft > 2*lifter_order+1 lifter(end-lifter_order+1:end) = 1; end % === 5. 应用提升器 === cepstrum_liftered = cepstrum .* lifter; % === 6. 重建频谱包络 === log_env = real(fft(cepstrum_liftered, nfft)); env = exp(log_env(1:num_bins, :)); % 取回单边谱 % === 7. 数值稳定性处理 === env(env < eps) = eps; % 避免零值 end % 进度更新函数 function update_progress(progress, message) if nargin >= 1 set(progress_bar, 'XData', [0, progress, progress, 0]); end if nargin >= 2 set(progress_text, 'String', message); set(status_text, 'String', message); end if nargin == 1 set(progress_text, 'String', sprintf('%.0f%%', progress*100)); end % 强制刷新界面 drawnow limitrate; end % 播放音频函数 function play_audio(audio, fs) if processing, return; end if isempty(audio) errordlg('音频数据为空!', '错误'); return; end try player = audioplayer(audio, fs); play(player); set(status_text, 'String', '正在播放音频...'); update_diag(['播放音频: ', num2str(length(audio)/fs), '秒'], true); catch e errordlg(['播放失败: ', e.message], '极错误'); update_diag(['播放错误: ', e.message], true); end end % 保存音频函数 function save_audio(~, ~) if processing errordlg('处理正在进行中,请稍后保存', '错误'); return; end if isempty(converted_audio) errordlg('没有转换后的音频可保存!', '错误'); return; end [file, path] = uiputfile('*.wav', '保存转换音频'); if isequal(file, 0), return; end set(status_text, 'String', '正在保存音频...'); update_diag(['开始保存: ', file], true); try % 直接保存音频 filename = fullfile(path, file); audiowrite(filename, converted_audio, fs); set(status_text, 'String', ['已保存: ', file]); update_diag(['音频已保存: ', filename], true); catch e errordlg(['保存失败: ', e.message], '极错误'); update_diag(['保存错误: ', e.message], true); end end % 简化版频谱显示函数(不再包含包络计算) function show_spectrum(ax, audio, fs, params, title_str) try % 计算STFT [S, f, t] = optimized_stft(audio, params, []); % 处理频谱数据 mag = abs(S); spec_data = 10*log10(mag + eps); % === 增强的维度验证 === % 确保频率向量是列向量 if ~iscolumn(f) f = f(:); end % 确保时间向量是行向量 if ~isrow(t) t = t(:)'; end % === 维度一致性检查 === if size(spec_data, 1) ~= length(f) || size(spec_data, 2) ~= length(t) min_bins = min(size(spec_data, 1), length(f)); min_frames = min(size(spec_data, 2), length(t)); spec_data = spec_data(1:min_bins, 1:min_frames); f = f(1:min_bins); t = t(1:min_frames); update_diag(sprintf('维度自动调整: spec_data(%d×%d), f(%d), t(%d)',... size(spec_data,1), size(spec_data,2), length(f), length(t))); end % === 坐标值验证 === % 确保频率在合理范围内 nyquist = fs/2; if any(f > nyquist) f(f > nyquist) = nyquist; end % 清除旧内容 cla(ax); % === 绘制频谱图 === imagesc(ax, t, f, spec_data); % === 设置坐标轴属性 === set(ax, 'YDir', 'normal'); % 低频在底部 axis(ax, 'tight'); % 自动调整坐标轴范围 % === 设置显示属性 === title(ax, title_str); xlabel(ax, '时间 (s)'); ylabel(ax, '频率 (Hz)'); colorbar(ax); colormap(ax, 'jet'); % 设置频率范围 max_freq = min(fs/2, max(f)); ylim(ax, [min(f), max_freq]); % 添加诊断信息 update_diag(sprintf('频谱显示成功: %s (尺寸: %d×%d)', title_str, size(spec_data,1), size(spec_data,2))); catch e % 详细的错误信息 dim_info = sprintf('维度: spec_data(%d×%d), f(%d), t(%d)',... size(spec_data,1), size(spec_data,2), length(f), length(t)); err_msg = sprintf('频谱错误: %s\n%s', e.message, dim_info); % 显示错误信息 cla(ax); text(ax, 0.5, 0.5, err_msg, ... 'HorizontalAlignment', 'center', ... 'FontSize', 8, 'Color', 'red'); title(ax, [title_str, ' (错误)']); % 在诊断信息中记录详细错误 update_diag(['频谱显示错误: ' err_msg], true); end end end %% === 核心音频处理函数 === function [mag, phase, f, t] = optimized_stft(x, params, progress_callback) % 优化的短时傅里叶变换(STFT)实现 % 输入: % x - 时域信号 % params - 参数结构体 (包含窗长、重叠、FFT点数、窗函数等) % progress_callback - 进度回调函数 % 输出: % mag - 幅度谱 (单边谱) % phase - 相位谱 (单边谱) % f - 频率向量 (Hz) % t - 时间向量 (秒) % === 参数提取 === win_len = params.win_len; overlap = params.overlap; nfft = params.nfft; win_anal = params.window; fs = params.fs; hop_size = win_len - overlap; % === 计算STFT的帧数 === num_samples = length(x); num_frames = floor((num_samples - overlap) / hop_size); % === 初始化STFT矩阵 === S = zeros(nfft, num_frames); % 完整的双边谱 % 进度更新间隔 update_interval = max(1, floor(num_frames/10)); % === 分帧处理 === for frame_idx = 1:num_frames % 1. 计算当前帧的起始和结束索引 start_idx = (frame_idx - 1) * hop_size + 1; end_idx = start_idx + win_len - 1; % 边界处理:如果最后一帧超出信号长度,则截断 if end_idx > num_samples frame = [x(start_idx:end); zeros(end_idx - num_samples, 1)]; else frame = x(start_idx:end_idx); end % 2. 加窗 frame_win = frame .* win_anal; % 3. FFT S_frame = fft(frame_win, nfft); S(:, frame_idx) = S_frame; % 4. 进度更新 if ~isempty(progress_callback) && mod(frame_idx, update_interval) == 0 progress_callback(frame_idx/num_frames * 0.2, ... sprintf('STFT计算: %d/%d', frame_idx, num_frames)); end end % === 计算单边谱 === if rem(nfft, 2) % 奇数点FFT num_bins = (nfft+1)/2; else num_bins = nfft/2 + 1; end S_half = S(1:num_bins, :); % 单边谱 % 幅度和相位 mag = abs(S_half); phase = angle(S_half); % 频率向量 f = (0:num_bins-1) * fs / nfft; % 时间向量 t = (0:num_frames-1) * hop_size / fs; end function x_recon = optimized_istft(mag, phase, params, progress_callback) % 优化的逆短时傅里叶变换(ISTFT)实现 % 输入: % mag - 幅度谱 (单边谱) % phase - 相位谱 (单边谱) % params - 参数结构体 % progress_callback - 进度回调函数 % 输出: % x_recon - 重建的时域信号 % === 参数提取 === nfft = params.nfft; win_len = params.win_len; hop_size = win_len - params.overlap; win_synth = params.win_synthesis; [num_bins, num_frames] = size(mag); % 计算信号总长度 total_samples = (num_frames - 1) * hop_size + win_len; x_recon = zeros(total_samples, 1); % 进度更新间隔 update_interval = max(1, floor(num_frames/10)); % === 重建复数频谱 === S_half = mag .* exp(1i * phase); % === 创建双边谱 === S_full = zeros(nfft, num_frames); if rem(nfft, 2) % 奇数点FFT S_full(1:num_bins, :) = S_half; S_full(num_bins+1:end, :) = conj(S_half(end:-1:2, :)); else % 偶数点FFT S_full(1:num_bins, :) = S_half; % 注意:Nyquist点处理 S_full(num_bins+1:end, :) = conj(S_half(end-1:-1:2, :)); end % === 执行逆FFT和重叠相加 === for frame_idx = 1:num_frames % 1. 逆FFT frame = real(ifft(S_full(:, frame_idx), nfft)); % 2. 应用合成窗 frame_win = frame(1:win_len) .* win_synth; % 3. 计算位置并叠加 start_idx = (frame_idx - 1) * hop_size + 1; end_idx = start_idx + win_len - 1; % 确保不越界 if end_idx > total_samples end_idx = total_samples; frame_win = frame_win(1:(end_idx - start_idx + 1)); end % 重叠相加 x_recon(start_idx:end_idx) = x_recon(start_idx:end_idx) + frame_win; % 4. 进度更新 if ~isempty(progress_callback) && mod(frame_idx, update_interval) == 0 progress_callback(frame_idx/num_frames * 0.2, ... sprintf('ISTFT重建: %d/%d', frame_idx, num_frames)); end end % === 归一化处理 === % 计算重叠因子 overlap_factor = win_len / hop_size; % 计算归一化窗口 norm_win = zeros(total_samples, 1); for i = 1:num_frames start_idx = (i - 1) * hop_size + 1; end_idx = min(start_idx + win_len - 1, total_samples); norm_win(start_idx:end_idx) = norm_win(start_idx:end_idx) + win_synth(1:(end_idx-start_idx+1)).^2; end % 避免除以零 norm_win(norm_win < eps) = eps; % 应用归一化 x_recon = x_recon ./ norm_win; end 以上是全部代码,但是运行有一些问题,主要是刚才测试时显示输出参数过多,并且STFT的调用有一些问题,请重写这些问题,其他的如控件结构等请不要更改,然后提供完整的包含所有部分的代码
06-15
在只有一条折痕的情况下(即此时的参数条件),计算每个中间折痕处的折叠角:% main_multiple_folds.m - 多折痕曲纹折纸机构运动学建模 % Multi-fold curved origami mechanism kinematic modeling clear; clc; close all %% 参数设置 - Parameter Settings % 思路:基于单个折痕的扩展逻辑,将折痕横向扩展到多条 % Concept: Extend single fold logic to multiple parallel folds % 生成扇形角度 - Generate sector angles theta_deg = []; N_vertices =13; % 每条折痕的顶点数 - Number of vertices per fold N_folds =1; % 折痕数量 - Number of folds % %直纹折纸 % %为每个顶点生成扇形角度 - Generate sector angles for each vertex % theta_deg(:,1) = 95*ones(N_vertices,1) + rand(N_vertices,1)*0; % theta_deg(:,2) = 85*ones(N_vertices,1) + rand(N_vertices,1)*0; % theta_deg(:,3) = 60*ones(N_vertices,1) + rand(N_vertices,1)*0; % theta_deg(:,4) = 120*ones(N_vertices,1) + rand(N_vertices,1)*0; % theta_deg(:,3) = 180*ones(N_vertices,1) - theta_deg(:,1); % 条件(2) % theta_deg(:,4) = 360*ones(N_vertices,1) - (theta_deg(:,1)+theta_deg(:,2)+theta_deg(:,3)); % %曲纹折纸(常曲率) % s=10; % ds=0.1; % N_vertices=s/ds+1; % kl=-0.1;kr=kl;%可折展 % phl=0/180*pi;phr=phl; % rr=1/abs(kr);rl=1/abs(kl); % dthr=2*atan(ds/(2*rr));dthl=2*atan(ds/(2*rl)); % theta_deg(:,1)=rad2deg(pi/2+sign(kr)*(dthr/2)+phr)*ones(N_vertices,1); % theta_deg(:,2)=rad2deg(pi/2+sign(kr)*(dthr/2)-phr)*ones(N_vertices,1); % theta_deg(:,3)=rad2deg(pi/2-sign(kl)*(dthl/2)+phl)*ones(N_vertices,1); % theta_deg(:,4)=rad2deg(pi/2-sign(kl)*(dthl/2)-phl)*ones(N_vertices,1); %曲纹折纸(变曲率) s=15; B=30;%离散程度 ds=s/B; N_vertices=B+1; for i=1:N_vertices kl(i)=(0.05*ds*(i-1))^3.1; kr=kl; phl=0/180*pi;phr=phl; rr(i)=1/abs(kr(i));rl(i)=1/abs(kl(i)); dthr(i)=2*atan(ds/(2*rr(i)));dthl(i)=2*atan(ds/(2*rl(i))); theta_deg(i,1)=rad2deg(pi/2+sign(kr(i))*(dthr(i)/2)+phr); theta_deg(i,2)=rad2deg(pi/2+sign(kr(i))*(dthr(i)/2)-phr); theta_deg(i,3)=rad2deg(pi/2-sign(kl(i))*(dthl(i)/2)+phl); theta_deg(i,4)=rad2deg(pi/2-sign(kl(i))*(dthl(i)/2)-phl); end th0=theta_deg(1,1);th1 = theta_deg(1,2);th2 = theta_deg(1,3); I=1e-10; %% 主循环参数 - Main loop parameters for i_total =160 % % 折叠角度参数 - Folding angle parameter % 基本参数设置 - Basic parameter setup %l_initial=[1,0.6,1]*0.1; % 初始长度 - Initial lengths l_initial=[1,ds,1]*0.1; % 初始长度 - Initial lengths %l_initial=[1 ,1.5,1]*0.1; % 初始长度 - Initial lengths c_scale = 1; % 缩放因子 - Scaling factor %曲纹折纸不适合非等距,因为改变了每条折痕曲率 rho_initial_deg = i_total; % 初始折叠角度 - Initial folding angle sigma_geom_choice = -1; % 几何选择参数 - Geometry choice parameter % 转换为弧度 - Convert to radians theta_rad = deg2rad(theta_deg); rho_central_rad = deg2rad(i_total); %% 计算初始折痕 - Calculate initial fold [P_center_0, P_left_0, P_right_0, actual_N_vertices] = calculate_3d_strip_geometry(... theta_rad, rho_central_rad, l_initial, c_scale, sigma_geom_choice, N_vertices); if isempty(P_center_0) fprintf('Failed to calculate initial 3D geometry. Check parameters.\n'); return; end %% 多折痕扩展 - Multiple fold expansion % 存储所有折痕的数据 - Store data for all folds P_center_all = cell(N_folds, 1); % 中心线点 - Center line points P_left_all = cell(N_folds, 1); % 左边界点 - Left boundary points P_right_all = cell(N_folds, 1); % 右边界点 - Right boundary points % 第一条折痕(原始折痕)- First fold (original fold) P_center_all{1} = P_center_0; P_left_all{1} = P_left_0; P_right_all{1} = P_right_0; % 生成多条折痕 - Generate multiple folds for fold_idx = 2:N_folds % 获取前一条折痕作为参考 - Get previous fold as reference PR_prev_center = P_right_all{fold_idx-1}; PR_prev_left = P_center_all{fold_idx-1}; %% 计算前一条折痕的角度参数 - Calculate angle parameters for previous fold PR_th2_ = zeros(1, length(PR_prev_center)-1); PR_th3_ = zeros(1, length(PR_prev_center)-1); % a=P_right_0(:,2)-P_right_0(:,1); % b=P_center_0(:,1)-P_right_0(:,1); % dot_product = dot(a, b); % norm_a = norm(a); % norm_b = norm(b); % th10 = acosd(dot_product / (norm_a * norm_b)); a=PR_prev_center(:,1)-PR_prev_center(:,2); b=PR_prev_left (:,2)-PR_prev_center(:,2); dot_product = dot(a, b); norm_a = norm(a); norm_b = norm(b); th10 = 180-acosd(dot_product / (norm_a * norm_b)); for i_ = 1:length(PR_prev_center)-1 % 计算向量夹角 - Calculate vector angles A = (PR_prev_center(:,i_+1) - PR_prev_center(:,i_)); B = (PR_prev_left(:,i_) - PR_prev_center(:,i_)); dot_product = dot(A, B); norm_A = norm(A); norm_B = norm(B); cos_theta(i_) = dot_product / (norm_A * norm_B); PR_th2_(i_) = acos(cos_theta(i_)); end for i_ = 2:length(PR_prev_center) A = (PR_prev_center(:,i_-1) - PR_prev_center(:,i_)); B1 = (PR_prev_left(:,i_) - PR_prev_center(:,i_)); dot_product1 = dot(A, B1); norm_B1 = norm(B1); cos_theta(i_) = dot_product1 / (norm_A * norm_B1); PR_th3_(i_) = acos(cos_theta(i_)); end % c=P_left_0(:,end-1)-P_left_0(:,end); d=P_center_0(:,end)-P_left_0(:,end); dot_product = dot(c, d); norm_c = norm(c); norm_d = norm(d); th00 = acosd(dot_product / (norm_c * norm_d)); %% 计算新折痕的角度参数 - Calculate new fold angle parameters PR_th2 = rad2deg(PR_th2_); PR_th0 =180- PR_th2; %% 生成新折痕的右边界 - Generate right boundary for new fold % 计算第一个点的位置 - Calculate position of first point A = (PR_prev_center(:,2) - PR_prev_center(:,1)); B = (PR_prev_left(:,1) - PR_prev_center(:,1)); % 计算法向量 - Calculate normal vector PR_normal_Vector = cross(A, B); if rem(fold_idx,2)==1 sign_flag = -1; PR_th1 = th1; PR_th0(N_vertices )=theta_deg(N_vertices,1); else sign_flag = 1; PR_th1 = th10; PR_th0(N_vertices )=PR_th0(N_vertices-1 ); end % 旋转法向量 - Rotate normal vector PR_right_normal_Vector = (RotationofRandL(PR_prev_center(:,1), ... PR_prev_center(:,1) + PR_normal_Vector, A, deg2rad(sign_flag*i_total))) - PR_prev_center(:,1)'; % 计算旋转后的点 - Calculate rotated point PR_right_ = RotationofRandL(PR_prev_center(:,1), PR_prev_center(:,2), ... PR_right_normal_Vector/norm(PR_right_normal_Vector), -deg2rad(PR_th1)); % 新折痕的右边界点 - Right boundary points for new fold PR_right = zeros(3, N_vertices); PR_right(:,1) = PR_prev_center(:,1) + l_initial(3) *... (PR_right_' - PR_prev_center(:,1)) / norm((PR_right_' - PR_prev_center(:,1))); % 迭代计算其余点 - Iteratively calculate remaining points for i = 2:N_vertices A = (PR_right(:,i-1) - PR_prev_center(:,i-1)); B = (PR_prev_center(:,i) - PR_prev_center(:,i-1)); PR_normal_Vector = cross(A, B); PR_right_normal_Vector = PR_normal_Vector / norm(PR_normal_Vector); PR_right_1 = RotationofRandL(PR_prev_center(:,i), PR_prev_center(:,i-1), ... PR_right_normal_Vector / norm(PR_right_normal_Vector), deg2rad(PR_th0(i))); PR_right(:,i) = PR_prev_center(:,i) + l_initial(3) * c_scale^(i-1) * ... (PR_right_1' - PR_prev_center(:,i)) / norm((PR_right_1' - PR_prev_center(:,i))); end % 存储新折痕数据 - Store new fold data P_center_all{fold_idx} = PR_prev_center; P_left_all{fold_idx} = PR_prev_left; P_right_all{fold_idx} = PR_right; % 打印进度信息 - Print progress information fprintf('第 %d 条折痕计算完成 - Fold %d calculation completed\n', fold_idx, fold_idx); end %% 可视化所有折痕 - Visualize all folds figure(122); clf; % 清除图形 - Clear figure hold on; axis equal; % 颜色映射 - Color mapping colors1 = hsv(N_folds); colors2 = jet(N_folds); for fold_idx = 1:N_folds P_center_curr = P_center_all{fold_idx}; P_left_curr = P_left_all{fold_idx}; P_right_curr = P_right_all{fold_idx}; if fold_idx == 1 first_point = P_center_curr(:,1); % 绘制坐标系标记 % X轴(红色) quiver3(first_point(1), first_point(2), first_point(3), 0.1, 0, 0, ... 'Color', 'r', 'LineWidth', 2, 'MaxHeadSize', 1.5, 'AutoScale', 'off'); % Y轴(绿色) quiver3(first_point(1), first_point(2), first_point(3), 0, 0.1, 0, ... 'Color', 'g', 'LineWidth', 2, 'MaxHeadSize', 1.5, 'AutoScale', 'off'); % Z轴(蓝色) quiver3(first_point(1), first_point(2), first_point(3), 0, 0, 0.1, ... 'Color', 'b', 'LineWidth', 2, 'MaxHeadSize', 1.5, 'AutoScale', 'off'); % 添加点标记 plot3(first_point(1), first_point(2), first_point(3), 'ko', ... 'MarkerSize', 5, 'MarkerFaceColor', 'y'); end % 绘制每个折痕的面板 - Draw panels for each fold for i = 1:N_vertices-1 % 左面板坐标 - Left panel coordinates x_l = [P_center_curr(1,i) P_center_curr(1,i+1) P_left_curr(1,i+1) P_left_curr(1,i) P_center_curr(1,i)]; y_l = [P_center_curr(2,i) P_center_curr(2,i+1) P_left_curr(2,i+1) P_left_curr(2,i) P_center_curr(2,i)]; z_l = [P_center_curr(3,i) P_center_curr(3,i+1) P_left_curr(3,i+1) P_left_curr(3,i) P_center_curr(3,i)]; % 右面板坐标 - Right panel coordinates x_r = [P_center_curr(1,i) P_center_curr(1,i+1) P_right_curr(1,i+1) P_right_curr(1,i) P_center_curr(1,i)]; y_r = [P_center_curr(2,i) P_center_curr(2,i+1) P_right_curr(2,i+1) P_right_curr(2,i) P_center_curr(2,i)]; z_r = [P_center_curr(3,i) P_center_curr(3,i+1) P_right_curr(3,i+1) P_right_curr(3,i) P_center_curr(3,i)]; % 绘制和填充面板 - Draw and fill panels % plot3(x_l, y_l, z_l, '.-', 'LineWidth', 2, 'Color', colors1(fold_idx,:)); % plot3(x_r, y_r, z_r, '-', 'LineWidth', 2, 'Color', colors2(fold_idx,:)); % plot3(x_l, y_l, z_l, '.'); % plot3(x_r, y_r, z_r, '*' ); % % 填充颜色 - Fill color fill3(x_l, y_l, z_l, colors1(fold_idx,:)); fill3(x_r, y_r, z_r, colors2(fold_idx,:)); end end axis off % % 设置图形属性 - Set figure properties % xlabel('X 坐标 - X Coordinate'); % ylabel('Y 坐标 - Y Coordinate'); % zlabel('Z 坐标 - Z Coordinate'); % title(sprintf('多折痕曲纹折纸机构 (%d 条折痕) - Multi-fold Curved Origami (%d folds)', N_folds, N_folds)); % % grid on; % material shiny; % light('Position',[0 0 0]); % alpha(1); % 设置透明度 %view(3) view(0,90); % % 添加图例 - Add legend % legend_entries = cell(N_folds, 1); % for fold_idx = 1:N_folds % legend_entries{fold_idx} = sprintf('折痕 %d - Fold %d', fold_idx, fold_idx); % end %% 验证几何一致性 - Verify geometric consistency fprintf('\n=== 几何一致性验证 - Geometric Consistency Verification ===\n'); for fold_idx = 1:N_folds P_center_curr = P_center_all{fold_idx}; P_right_curr = P_right_all{fold_idx}; % 计算共面性检验 - Calculate coplanarity test triple_scalars = zeros(1, length(P_center_curr)-1); for i_ = 1:length(P_center_curr)-1 P1 = P_center_curr(:,i_); P2 = P_center_curr(:,i_+1); P3 = P_right_curr(:,i_); P4 = P_right_curr(:,i_+1); % 计算混合积 - Calculate scalar triple product v1 = P2 - P1; v2 = P3 - P1; v3 = P4 - P1; triple_scalars(i_) = dot(v1, cross(v2, v3)); end % 输出验证结果 - Output verification results max_deviation = max(abs(triple_scalars)); fprintf('折痕 %d: 最大共面偏差 = %.2e - Fold %d: Max coplanarity deviation = %.2e\n', ... fold_idx, max_deviation, fold_idx, max_deviation); if max_deviation < 1e-10 fprintf(' ✓ 几何一致性良好 - Good geometric consistency\n'); else fprintf(' ⚠ 存在几何偏差 - Geometric deviation detected\n'); end end %% 角度分析 - Angle analysis % figure(123); % clf; % hold on; % % for fold_idx = 1 % % % 计算角度变化 - Calculate angle changes % P_center_curr = P_center_all{fold_idx}; % P_left_curr = P_left_all{fold_idx}; % % angles_th2 = zeros(1, length(P_center_curr)-1); % angles_th3 = zeros(1, length(P_center_curr)-1); % % for i_ = 1:length(P_center_curr)-1 % A = (P_center_curr(:,i_+1) - P_center_curr(:,i_)); % B = (P_left_curr(:,i_) - P_center_curr(:,i_)); % % dot_product = dot(A, B); % norm_A = norm(A); % norm_B = norm(B); % cos_theta = dot_product / (norm_A * norm_B); % % if i_ == 1 % angles_th2(i_) = acos(cos_theta); % angles_th3(i_) = 0; % else % B1 = (P_left_curr(:,i_+1) - P_center_curr(:,i_+1)); % dot_product1 = dot(-A, B1); % norm_B1 = norm(B1); % cos_theta1 = dot_product1 / (norm_A * norm_B1); % % angles_th2(i_) = acos(cos_theta); % angles_th3(i_) = acos(cos_theta1); % end % end % % % 绘制角度变化 - Plot angle changes % plot(rad2deg(angles_th2), 'o-', 'LineWidth', 2, 'Color', colors1(fold_idx,:), ... % 'DisplayName', sprintf('折痕 %d - θ2', fold_idx)); % plot(rad2deg(angles_th3), 's--', 'LineWidth', 2, 'Color', colors2(fold_idx,:), ... % 'DisplayName', sprintf('折痕 %d - θ3', fold_idx)); % % end % % xlabel('顶点索引 - Vertex Index'); % ylabel('角度 (度) - Angle (degrees)'); % title('多折痕角度分析 - Multi-fold Angle Analysis'); % legend('show'); % grid on; %% 输出总结信息 - Output summary information fprintf('\n=== 计算总结 - Calculation Summary ===\n'); fprintf('总折痕数量 - Total number of folds: %d\n', N_folds); fprintf('每条折痕顶点数 - Vertices per fold: %d\n', N_vertices); fprintf('计算完成 - Calculation completed successfully\n'); end fprintf('\n多折痕曲纹折纸机构建模完成!- Multi-fold curved origami modeling completed!\n'); %% === 添加代码整合所有点坐标到单一矩阵 === % 计算总点数 (每条折痕有3类点 * N_vertices个顶点) totalPoints = N_folds * N_vertices * 3; % 初始化坐标矩阵 (3行: x,y,z; totalPoints列) allPointsMatrix = zeros(3, totalPoints); % 当前索引指针 currentIdx = 1; % 遍历所有折痕 for foldIdx = 1:N_folds % 当前折痕的点集 P_center = P_center_all{foldIdx}; P_left = P_left_all{foldIdx}; PR_right = P_right_all{foldIdx}; % 将中心点添加到矩阵 allPointsMatrix(:, currentIdx:currentIdx+N_vertices-1) = P_left; currentIdx = currentIdx + N_vertices; % 将左边界点添加到矩阵 allPointsMatrix(:, currentIdx:currentIdx+N_vertices-1) = P_center; currentIdx = currentIdx + N_vertices; % 将右边界点添加到矩阵 allPointsMatrix(:, currentIdx:currentIdx+N_vertices-1) = PR_right; currentIdx = currentIdx + N_vertices; end allPointsMatrix=allPointsMatrix'; for i=2:N_folds if i==2 for j=1:2*N_vertices allPointsMatrix((i-1)*3*N_vertices,:)=[]; end else for j=1:2*N_vertices allPointsMatrix((i+1)*N_vertices,:)=[]; end end end a1=allPointsMatrix(:,1); a2=allPointsMatrix(:,2); allPointsMatrix1=allPointsMatrix(:,1:2);
07-16
根据下面两段代码优化解决优化后的“TDMFeedbackController”的模块代码“无法识别的字段名称 "priorities"。”的问题,给我优化后完整的代码第一段:classdef TDMApp8888 < matlab.apps.AppBase properties (Access = public) UIFigure matlab.ui.Figure % 左侧控制面板 ControlPanel matlab.ui.container.Panel SignalConfigPanel matlab.ui.container.Panel NumSignalsEditField matlab.ui.control.NumericEditField NumSignalsLabel matlab.ui.control.Label SignalTypeDropDown matlab.ui.control.DropDown SignalTypeLabel matlab.ui.control.Label FrequencyEditField matlab.ui.control.NumericEditField FrequencyLabel matlab.ui.control.Label AddSignalButton matlab.ui.control.Button RemoveSignalButton matlab.ui.control.Button SignalsListBox matlab.ui.control.ListBox SignalsListLabel matlab.ui.control.Label % 新增载波频率控件 CarrierFrequencyLabel matlab.ui.control.Label CarrierFrequencyEditField matlab.ui.control.NumericEditField StrategyPanel matlab.ui.container.Panel FixedRadioButton matlab.ui.control.RadioButton PriorityRadioButton matlab.ui.control.RadioButton StrategyButtonGroup matlab.ui.container.ButtonGroup TotalSlotsEditField matlab.ui.control.NumericEditField TotalSlotsLabel matlab.ui.control.Label % 编码配置控件 EncodingPanel matlab.ui.container.Panel EncodingTypeDropDown matlab.ui.control.DropDown EncodingTypeLabel matlab.ui.control.Label QuantBitsEditField matlab.ui.control.NumericEditField QuantBitsLabel matlab.ui.control.Label StepSizeEditField matlab.ui.control.NumericEditField StepSizeLabel matlab.ui.control.Label % 调制配置控件 ModulationPanel matlab.ui.container.Panel ModulationTypeDropDown matlab.ui.control.DropDown ModulationTypeLabel matlab.ui.control.Label % 帧配置控件 FrameConfigPanel matlab.ui.container.Panel FrameHeaderEditField matlab.ui.control.EditField FrameHeaderLabel matlab.ui.control.Label CRCCheckBox matlab.ui.control.CheckBox % 右侧控制面板 SimulationPanel matlab.ui.container.Panel RunSimulationButton matlab.ui.control.Button RunFeedbackButton matlab.ui.control.Button RunAnalysisButton matlab.ui.control.Button SNREditField matlab.ui.control.NumericEditField SNRLabel matlab.ui.control.Label IterationsEditField matlab.ui.control.NumericEditField IterationsLabel matlab.ui.control.Label DurationEditField matlab.ui.control.NumericEditField DurationLabel matlab.ui.control.Label SamplingRateEditField matlab.ui.control.NumericEditField SamplingRateLabel matlab.ui.control.Label StatusLabel matlab.ui.control.Label ProgressBar matlab.ui.control.Lamp % 结果显示区域 ResultsTabGroup matlab.ui.container.TabGroup SignalsTab matlab.ui.container.Tab OriginalAxes matlab.ui.control.UIAxes TDMAxes matlab.ui.control.UIAxes DemuxAxes matlab.ui.control.UIAxes SyncedAxes matlab.ui.control.UIAxes PerformanceTab matlab.ui.container.Tab BERAxes matlab.ui.control.UIAxes ParametersTab matlab.ui.container.Tab ParametersTextArea matlab.ui.control.TextArea end properties (Access = private) signals % 存储信号配置的cell数组 params % 系统参数结构体 tdmSystem % TDMSystem实例 controller % TDMFeedbackController实例 analyzer % TDMPerformanceAnalyzer实例 end methods (Access = private) function updateParams(app) % 更新系统参数 app.params = struct(); % 确保所有UI组件都存在且有效 try app.params.fs = app.SamplingRateEditField.Value; app.params.duration = app.DurationEditField.Value; app.params.numSignals = numel(app.signals); app.params.snrDb = app.SNREditField.Value; app.params.iterations = app.IterationsEditField.Value; app.params.totalSlots = app.TotalSlotsEditField.Value; % 编码参数 if isvalid(app.EncodingTypeDropDown) app.params.encodingType = app.EncodingTypeDropDown.Value; else app.params.encodingType = 'PCM'; % 默认值 end app.params.quantBits = app.QuantBitsEditField.Value; app.params.stepSize = app.StepSizeEditField.Value; % 调制参数 if isvalid(app.ModulationTypeDropDown) app.params.modulationType = app.ModulationTypeDropDown.Value; else app.params.modulationType = 'BPSK'; % 默认值 end % 帧配置 if isvalid(app.FrameHeaderEditField) app.params.frameHeader = app.FrameHeaderEditField.Value; else app.params.frameHeader = 'A5A5'; % 默认值 end if isvalid(app.CRCCheckBox) app.params.useCRC = app.CRCCheckBox.Value; else app.params.useCRC = true; % 默认值 end % 时隙分配策略 if isvalid(app.FixedRadioButton) && isvalid(app.PriorityRadioButton) if app.FixedRadioButton.Value app.params.strategy = 'fixed'; else app.params.strategy = 'priority'; end else app.params.strategy = 'priority'; % 默认值 end % 设置信号调制方式和时钟漂移 app.params.signalModulations = cell(1, app.params.numSignals); app.params.clockDrift = zeros(1, app.params.numSignals); for i = 1:app.params.numSignals if i <= numel(app.signals) app.params.signalModulations{i} = app.signals{i}.modulation; else app.params.signalModulations{i} = 'BPSK'; % 默认值 end % 设置时钟漂移 (确保长度与信号数量匹配) if i == 1 app.params.clockDrift(i) = 0.001; elseif i == 2 app.params.clockDrift(i) = 0.002; else app.params.clockDrift(i) = -0.001; end end catch ME % 错误处理:记录错误并恢复默认值 disp('更新参数时出错:'); disp(ME.message); app.params = struct('fs', 1000, 'duration', 1, 'numSignals', 0); end end function generateSignals(app) % 生成信号数据 (使用linspace确保精确采样点) if isempty(app.params) || ~isfield(app.params, 'fs') || ~isfield(app.params, 'duration') return; % 参数无效,跳过 end t = linspace(0, app.params.duration, app.params.fs * app.params.duration); app.tdmSystem.originalSignals = zeros(app.params.numSignals, length(t)); app.tdmSystem.signalInfo = cell(app.params.numSignals, 1); for i = 1:app.params.numSignals if i <= numel(app.signals) sigConfig = app.signals{i}; else % 默认值包含载波频率 sigConfig = struct('type', '正弦波', 'frequency', 50, ... 'modulation', 'BPSK', 'carrierFrequency', 1000); end baseSignal = []; % 存储基带信号 switch sigConfig.type case '正弦波' freq = sigConfig.frequency; baseSignal = sin(2*pi*freq*t); baseInfo = sprintf('正弦波 (%dHz)', freq); case '方波' freq = sigConfig.frequency; baseSignal = square(2*pi*freq*t); baseInfo = sprintf('方波 (%dHz)', freq); case '随机噪声' baseSignal = randn(1, length(t)); baseInfo = '随机噪声'; case '锯齿波' freq = sigConfig.frequency; baseSignal = sawtooth(2*pi*freq*t); baseInfo = sprintf('锯齿波 (%dHz)', freq); case '脉冲信号' freq = sigConfig.frequency; duty = 0.3; % 占空比 pulseTrain = pulstran(t, 0:1/freq:app.params.duration, ... 'rectpuls', duty/freq); % 确保长度匹配 if length(pulseTrain) > length(t) baseSignal = pulseTrain(1:length(t)); else baseSignal = zeros(1, length(t)); baseSignal(1:length(pulseTrain)) = pulseTrain; end baseInfo = sprintf('脉冲信号 (%dHz)', freq); end % ========== 应用载波调制 ========== carrierFreq = sigConfig.carrierFrequency; switch sigConfig.modulation case 'AM' % 幅度调制: (1 + m(t)) * cos(2πf_c t) modulatedSignal = (1 + 0.5*baseSignal) .* cos(2*pi*carrierFreq*t); modInfo = sprintf('AM调制 (%dHz载波)', carrierFreq); case 'FM' % 频率调制: cos(2πf_c t + 2πΔf ∫m(t)dt) kf = 0.1; % 调制指数 integral_m = cumsum(baseSignal) / app.params.fs; modulatedSignal = cos(2*pi*carrierFreq*t + 2*pi*kf*integral_m); modInfo = sprintf('FM调制 (%dHz载波)', carrierFreq); case 'PM' % 相位调制: cos(2πf_c t + k_p m(t)) kp = 1; % 相位偏移常数 modulatedSignal = cos(2*pi*carrierFreq*t + kp*baseSignal); modInfo = sprintf('PM调制 (%dHz载波)', carrierFreq); otherwise % 无调制或未知调制类型 modulatedSignal = baseSignal; modInfo = '无调制'; end app.tdmSystem.originalSignals(i, :) = modulatedSignal; app.tdmSystem.signalInfo{i} = [baseInfo ', ' modInfo]; end % 设置编码参数 if isfield(app.params, 'encodingType') && isfield(app.params, 'quantBits') && isfield(app.params, 'stepSize') app.tdmSystem.encodingParams = struct(... 'type', app.params.encodingType, ... 'quantBits', app.params.quantBits, ... 'stepSize', app.params.stepSize); else app.tdmSystem.encodingParams = struct('type', 'PCM', 'quantBits', 8, 'stepSize', 0.05); % 默认值 end % 设置调制参数 if isfield(app.params, 'modulationType') && isfield(app.params, 'signalModulations') app.tdmSystem.modulationParams = struct(... 'type', app.params.modulationType, ... 'signalModulations', {app.params.signalModulations}); else app.tdmSystem.modulationParams = struct('type', 'BPSK', 'signalModulations', {{'BPSK', 'BPSK'}}); % 默认值 end % 设置帧配置 if isfield(app.params, 'frameHeader') && isfield(app.params, 'useCRC') app.tdmSystem.frameConfig = struct(... 'header', app.params.frameHeader, ... 'useCRC', app.params.useCRC); else app.tdmSystem.frameConfig = struct('header', 'A5A5', 'useCRC', true); % 默认值 end end function updateParametersDisplay(app) % 更新参数显示 try paramText = sprintf('系统参数:\n'); if isfield(app.params, 'fs') paramText = [paramText sprintf('采样频率: %d Hz\n', app.params.fs)]; end if isfield(app.params, 'duration') paramText = [paramText sprintf('信号持续时间: %.2f 秒\n', app.params.duration)]; end if isfield(app.params, 'numSignals') paramText = [paramText sprintf('信号源数量: %d\n', app.params.numSignals)]; end if isfield(app.params, 'snrDb') paramText = [paramText sprintf('信噪比: %d dB\n', app.params.snrDb)]; end if isfield(app.params, 'strategy') paramText = [paramText sprintf('时隙分配策略: %s\n', app.params.strategy)]; end if isfield(app.params, 'totalSlots') paramText = [paramText sprintf('总时隙数量: %d\n', app.params.totalSlots)]; end if isfield(app.params, 'iterations') paramText = [paramText sprintf('仿真迭代次数: %d\n', app.params.iterations)]; end % 编码参数 paramText = [paramText sprintf('\n编码配置:\n')]; if isfield(app.params, 'encodingType') paramText = [paramText sprintf(' 编码类型: %s\n', app.params.encodingType)]; end if isfield(app.params, 'quantBits') paramText = [paramText sprintf(' 量化位数: %d bits\n', app.params.quantBits)]; end if isfield(app.params, 'stepSize') paramText = [paramText sprintf(' 步长: %.4f\n', app.params.stepSize)]; end % 调制参数 paramText = [paramText sprintf('\n调制配置:\n')]; if isfield(app.params, 'modulationType') paramText = [paramText sprintf(' 系统调制类型: %s\n', app.params.modulationType)]; end % 帧配置 paramText = [paramText sprintf('\n帧配置:\n')]; if isfield(app.params, 'frameHeader') paramText = [paramText sprintf(' 帧头: %s\n', app.params.frameHeader)]; end if isfield(app.params, 'useCRC') paramText = [paramText sprintf(' CRC校验: %s\n', ifelse(app.params.useCRC, '启用', '禁用'))]; end % 信号信息 - 包含载波频率 paramText = [paramText sprintf('\n信号配置:\n')]; for i = 1:min(app.params.numSignals, numel(app.signals)) sig = app.signals{i}; paramText = [paramText sprintf('信号 %d: %s (基带频率: %d Hz, 调制: %s, 载波频率: %d Hz)\n', ... i, sig.type, sig.frequency, sig.modulation, sig.carrierFrequency)]; end app.ParametersTextArea.Value = paramText; catch ME disp('更新参数显示时出错:'); disp(ME.message); app.ParametersTextArea.Value = '系统参数显示错误'; end end function plotSignals(app) % 绘制原始信号 try t = linspace(0, app.params.duration, app.params.fs * app.params.duration); cla(app.OriginalAxes); hold(app.OriginalAxes, 'on'); colors = lines(app.params.numSignals); for i = 1:min(app.params.numSignals, size(app.tdmSystem.originalSignals, 1)) plot(app.OriginalAxes, t, app.tdmSystem.originalSignals(i, 1:length(t)), ... 'Color', colors(i, :), 'DisplayName', app.tdmSystem.signalInfo{i}); end hold(app.OriginalAxes, 'off'); legend(app.OriginalAxes, 'Location', 'best'); title(app.OriginalAxes, '原始信号'); xlabel(app.OriginalAxes, '时间 (s)'); ylabel(app.OriginalAxes, '幅度'); grid(app.OriginalAxes, 'on'); % 绘制TDM信号(支持复数信号) cla(app.TDMAxes); if ~isempty(app.tdmSystem.tdmSignal) && isfield(app.params, 'duration') t_tdm = linspace(0, app.params.duration, length(app.tdmSystem.tdmSignal)); plot(app.TDMAxes, t_tdm, real(app.tdmSystem.tdmSignal)); hold(app.TDMAxes, 'on'); plot(app.TDMAxes, t_tdm, imag(app.tdmSystem.tdmSignal)); hold(app.TDMAxes, 'off'); legend(app.TDMAxes, {'实部', '虚部'}, 'Location', 'best'); end title(app.TDMAxes, ['TDM复用信号 (' app.params.strategy '策略)']); xlabel(app.TDMAxes, '时间 (s)'); ylabel(app.TDMAxes, '幅度'); grid(app.TDMAxes, 'on'); % 绘制解复用信号 cla(app.DemuxAxes); hold(app.DemuxAxes, 'on'); for i = 1:min(app.params.numSignals, size(app.tdmSystem.demuxSignals, 1)) if size(app.tdmSystem.demuxSignals, 2) >= length(t) plot(app.DemuxAxes, t,app.tdmSystem.demuxSignals(i, 1:length(t)), ... 'Color', colors(i, :), 'DisplayName', ['信号 ' num2str(i)]); end end hold(app.DemuxAxes, 'off'); legend(app.DemuxAxes, 'Location', 'best'); title(app.DemuxAxes, '解复用信号'); xlabel(app.DemuxAxes, '时间 (s)'); ylabel(app.DemuxAxes, '幅度'); grid(app.DemuxAxes, 'on'); % 绘制同步后信号 cla(app.SyncedAxes); hold(app.SyncedAxes, 'on'); for i = 1:min(app.params.numSignals, size(app.tdmSystem.syncedSignals, 1)) if size(app.tdmSystem.syncedSignals, 2) >= length(t) plot(app.SyncedAxes, t, app.tdmSystem.syncedSignals(i, 1:length(t)), ... 'Color', colors(i, :), 'DisplayName', app.tdmSystem.signalInfo{i}); end end hold(app.SyncedAxes, 'off'); legend(app.SyncedAxes, 'Location', 'best'); title(app.SyncedAxes, '同步后信号'); xlabel(app.SyncedAxes, '时间 (s)'); ylabel(app.SyncedAxes, '幅度'); grid(app.SyncedAxes, 'on'); % 绘制误码率和信噪比 cla(app.BERAxes); if ~isempty(app.tdmSystem.performance) && isfield(app.tdmSystem.performance, 'ber') && isfield(app.tdmSystem.performance, 'snr') yyaxis(app.BERAxes, 'left'); bar(app.BERAxes, 1:app.params.numSignals, app.tdmSystem.performance.ber, 0.6, 'FaceColor', [0.2 0.6 0.8]); ylabel(app.BERAxes, '误码率 (BER)'); yyaxis(app.BERAxes, 'right'); plot(app.BERAxes, 1:app.params.numSignals, app.tdmSystem.performance.snr, 'o-', 'LineWidth', 2, 'MarkerSize', 8); ylabel(app.BERAxes, 'SNR (dB)'); title(app.BERAxes, '信号质量分析'); xlabel(app.BERAxes, '信号编号'); grid(app.BERAxes, 'on'); xticks(app.BERAxes, 1:app.params.numSignals); legend(app.BERAxes, {'BER', 'SNR'}, 'Location', 'best'); end catch ME disp('绘图时出错:'); disp(ME.message); end end end % Callbacks that handle component events methods (Access = private) % Code that executes after component creation function startupFcn(app) % 初始化参数 app.signals = {}; app.params = struct(); app.StatusLabel.Text = '准备就绪'; app.ProgressBar.Color = 'green'; % 设置默认参数 app.SamplingRateEditField.Value = 1000; app.DurationEditField.Value = 1; app.SNREditField.Value = 20; app.IterationsEditField.Value = 5; app.TotalSlotsEditField.Value = 50; app.NumSignalsEditField.Value = 0; app.PriorityRadioButton.Value = true; % 编码参数默认值 app.EncodingTypeDropDown.Value = 'PCM'; app.QuantBitsEditField.Value = 8; app.StepSizeEditField.Value = 0.05; % 调制参数默认值 app.ModulationTypeDropDown.Value = 'BPSK'; % 帧配置默认值 app.FrameHeaderEditField.Value = 'A5A5'; app.CRCCheckBox.Value = true; % 载波频率默认值 app.CarrierFrequencyEditField.Value = 1000; % 更新参数显示 updateParams(app); updateParametersDisplay(app); end % Button pushed function: AddSignalButton function AddSignalButtonPushed(app, ~) % 添加新信号(包含载波频率) try signalType = app.SignalTypeDropDown.Value; frequency = app.FrequencyEditField.Value; modulationType = app.ModulationTypeDropDown.Value; % 获取调制类型 carrierFrequency = app.CarrierFrequencyEditField.Value; % 获取载波频率 if frequency <= 0 uialert(app.UIFigure, '基带频率必须大于0', '无效参数'); return; end if carrierFrequency <= 0 uialert(app.UIFigure, '载波频率必须大于0', '无效参数'); return; end newSignal = struct(... 'type', signalType, ... 'frequency', frequency, ... 'modulation', modulationType, ... 'carrierFrequency', carrierFrequency); % 添加载波频率 app.signals{end+1} = newSignal; app.NumSignalsEditField.Value = numel(app.signals); % 更新信号列表(包含载波频率) signalList = cell(1, numel(app.signals)); for i = 1:numel(app.signals) sig = app.signals{i}; signalList{i} = sprintf('信号 %d: %s (%d Hz基带, %s调制, %d Hz载波)', ... i, sig.type, sig.frequency, sig.modulation, sig.carrierFrequency); end app.SignalsListBox.Items = signalList; % 更新参数 updateParams(app); updateParametersDisplay(app); app.StatusLabel.Text = sprintf('已添加信号: %s (%d Hz基带, %s调制, %d Hz载波)', ... signalType, frequency, modulationType, carrierFrequency); catch ME app.StatusLabel.Text = ['添加信号错误: ' ME.message]; app.ProgressBar.Color = 'red'; end end % Button pushed function: RemoveSignalButton function RemoveSignalButtonPushed(app, ~) % 移除选中的信号(包含载波频率) try selectedIdx = app.SignalsListBox.Value; if isempty(selectedIdx) || selectedIdx > numel(app.signals) uialert(app.UIFigure, '请选择要删除的信号', '无选择'); return; end % 移除信号 removedSig = app.signals{selectedIdx}; app.signals(selectedIdx) = []; % 更新信号列表(包含载波频率) app.NumSignalsEditField.Value = numel(app.signals); signalList = cell(1, numel(app.signals)); for i = 1:numel(app.signals) sig = app.signals{i}; signalList{i} = sprintf('信号 %d: %s (%d Hz基带, %s调制, %d Hz载波)', ... i, sig.type, sig.frequency, sig.modulation, sig.carrierFrequency); end app.SignalsListBox.Items = signalList; % 如果没有信号,清除选择 if isempty(app.signals) app.SignalsListBox.Value = []; else app.SignalsListBox.Value = min(selectedIdx, numel(app.signals)); end % 更新参数 updateParams(app); updateParametersDisplay(app); app.StatusLabel.Text = sprintf('已移除信号: %s (%d Hz基带, %s调制, %d Hz载波)', ... removedSig.type, removedSig.frequency, removedSig.modulation, removedSig.carrierFrequency); catch ME app.StatusLabel.Text = ['移除信号错误: ' ME.message]; app.ProgressBar.Color = 'red'; end end % Value changed function: NumSignalsEditField function NumSignalsEditFieldValueChanged(app, ~) % 信号数量变化时更新 try updateParams(app); updateParametersDisplay(app); catch ME app.StatusLabel.Text = ['更新信号数量错误: ' ME.message]; app.ProgressBar.Color = 'red'; end end % Value changed function: SamplingRateEditField function SamplingRateEditFieldValueChanged(app, ~) try updateParams(app); updateParametersDisplay(app); catch ME app.StatusLabel.Text = ['更新采样率错误: ' ME.message]; app.ProgressBar.Color = 'red'; end end % Value changed function: DurationEditField function DurationEditFieldValueChanged(app, ~) try updateParams(app); updateParametersDisplay(app); catch ME app.StatusLabel.Text = ['更新持续时间错误: ' ME.message]; app.ProgressBar.Color = 'red'; end end % Value changed function: SNREditField function SNREditFieldValueChanged(app, ~) try updateParams(app); updateParametersDisplay(app); catch ME app.StatusLabel.Text = ['更新信噪比错误: ' ME.message]; app.ProgressBar.Color = 'red'; end end % Value changed function: IterationsEditField function IterationsEditFieldValueChanged(app, ~) try updateParams(app); updateParametersDisplay(app); catch ME app.StatusLabel.Text = ['更新迭代次数错误: ' ME.message]; app.ProgressBar.Color = 'red'; end end % Value changed function: TotalSlotsEditField function TotalSlotsEditFieldValueChanged(app, ~) try updateParams(app); updateParametersDisplay(app); catch ME app.StatusLabel.Text = ['更新总时隙数错误: ' ME.message]; app.ProgressBar.Color = 'red'; end end % Selection changed function: StrategyButtonGroup function StrategyButtonGroupSelectionChanged(app, ~) try updateParams(app); updateParametersDisplay(app); catch ME app.StatusLabel.Text = ['更新策略错误: ' ME.message]; app.ProgressBar.Color = 'red'; end end % 编码参数变化回调 function EncodingTypeDropDownValueChanged(app, ~) try updateParams(app); updateParametersDisplay(app); catch ME app.StatusLabel.Text = ['更新编码类型错误: ' ME.message]; app.ProgressBar.Color = 'red'; end end function QuantBitsEditFieldValueChanged(app, ~) try updateParams(app); updateParametersDisplay(app); catch ME app.StatusLabel.Text = ['更新量化位数错误: ' ME.message]; app.ProgressBar.Color = 'red'; end end function StepSizeEditFieldValueChanged(app, ~) try updateParams(app); updateParametersDisplay(app); catch ME app.StatusLabel.Text = ['更新步长错误: ' ME.message]; app.ProgressBar.Color = 'red'; end end % 调制参数变化回调 function ModulationTypeDropDownValueChanged(app, ~) try updateParams(app); updateParametersDisplay(app); catch ME app.StatusLabel.Text = ['更新调制方式错误: ' ME.message]; app.ProgressBar.Color = 'red'; end end function FrameHeaderEditFieldValueChanged(app, ~) try updateParams(app); updateParametersDisplay(app); catch ME app.StatusLabel.Text = ['更新帧头错误: ' ME.message]; app.ProgressBar.Color = 'red'; end end function CRCCheckBoxValueChanged(app, ~) try updateParams(app); updateParametersDisplay(app); catch ME app.StatusLabel.Text = ['更新CRC设置错误: ' ME.message]; app.ProgressBar.Color = 'red'; end end % Button pushed function: RunSimulationButton function RunSimulationButtonPushed(app, ~) % 运行基础仿真 app.StatusLabel.Text = '运行基础仿真...'; app.ProgressBar.Color = 'yellow'; drawnow; try % 更新参数 updateParams(app); % 检查信号配置 if app.params.numSignals == 0 uialert(app.UIFigure, '请至少添加一个信号', '无信号'); app.ProgressBar.Color = 'red'; return; end % 创建TDM系统 app.tdmSystem = TDMSystem(app.params); % 生成信号 generateSignals(app); % 运行仿真 app.tdmSystem = app.tdmSystem.runSimulation(); % 显示结果 plotSignals(app); updateParametersDisplay(app); app.StatusLabel.Text = '基础仿真完成!'; app.ProgressBar.Color = 'green'; catch ME app.StatusLabel.Text = ['基础仿真错误: ' ME.message]; app.ProgressBar.Color = 'red'; uialert(app.UIFigure, ME.message, '仿真错误'); end end % Button pushed function: RunFeedbackButton function RunFeedbackButtonPushed(app, ~) % 运行反馈控制仿真 app.StatusLabel.Text = '运行反馈控制仿真...'; app.ProgressBar.Color = 'yellow'; drawnow; try % 更新参数 updateParams(app); % 检查信号配置 if app.params.numSignals == 0 uialert(app.UIFigure, '请至少添加一个信号', '无信号'); app.ProgressBar.Color = 'red'; return; end % 创建反馈控制器 app.controller = TDMFeedbackController(app.params); % 生成信号 generateSignals(app); % 将生成的信号复制到控制器 app.controller.originalSignals = app.tdmSystem.originalSignals; app.controller.signalInfo = app.tdmSystem.signalInfo; app.controller.encodingParams = app.tdmSystem.encodingParams; app.controller.modulationParams = app.tdmSystem.modulationParams; app.controller.frameConfig = app.tdmSystem.frameConfig; % 运行反馈仿真 app.controller = app.controller.runSimulation(); % 显示结果 app.tdmSystem = app.controller; % 用于显示基本结果 plotSignals(app); updateParametersDisplay(app); app.StatusLabel.Text = '反馈控制仿真完成!'; app.ProgressBar.Color = 'green'; catch ME app.StatusLabel.Text = ['反馈控制错误: ' ME.message]; app.ProgressBar.Color = 'red'; uialert(app.UIFigure, ME.message, '仿真错误'); end end % Button pushed function: RunAnalysisButton function RunAnalysisButtonPushed(app, ~) % 运行性能分析 app.StatusLabel.Text = '运行性能分析...'; app.ProgressBar.Color = 'yellow'; drawnow; try % 更新参数 updateParams(app); % 检查信号配置 if app.params.numSignals == 0 uialert(app.UIFigure, '请至少添加一个信号', '无信号'); app.ProgressBar.Color = 'red'; return; end % 创建性能分析器 app.analyzer = TDMPerformanceAnalyzer(app.params); % 生成信号 generateSignals(app); % 将生成的信号复制到分析器 app.analyzer.originalSignals = app.tdmSystem.originalSignals; app.analyzer.signalInfo = app.tdmSystem.signalInfo; app.analyzer.encodingParams = app.tdmSystem.encodingParams; app.analyzer.modulationParams = app.tdmSystem.modulationParams; app.analyzer.frameConfig = app.tdmSystem.frameConfig; % 运行性能分析 app.analyzer = app.analyzer.runPerformanceAnalysis(); % 显示结果 app.tdmSystem = app.analyzer; % 用于显示基本结果 plotSignals(app); updateParametersDisplay(app); app.StatusLabel.Text = '性能分析完成!'; app.ProgressBar.Color = 'green'; catch ME app.StatusLabel.Text = ['性能分析错误: ' ME.message]; app.ProgressBar.Color = 'red'; uialert(app.UIFigure, ME.message, '仿真错误'); end end end % App initialization and construction methods (Access = private) % Create UIFigure and components function createComponents(app) % 创建主窗口 app.UIFigure = uifigure; app.UIFigure.Position = [100 100 1400 900]; app.UIFigure.Name = 'TDM通信系统仿真'; app.UIFigure.Scrollable = 'on'; % 创建控制面板容器 app.ControlPanel = uipanel(app.UIFigure); app.ControlPanel.Title = '控制面板'; app.ControlPanel.Position = [20 20 450 850]; % ==== 信号配置面板 ==== app.SignalConfigPanel = uipanel(app.ControlPanel); app.SignalConfigPanel.Title = '信号配置'; app.SignalConfigPanel.Position = [20 585 410 240]; % 增加高度以容纳载波频率控件 % 信号数量控件 app.NumSignalsLabel = uilabel(app.SignalConfigPanel); app.NumSignalsLabel.Position = [20 190 80 22]; app.NumSignalsLabel.Text = '信号数量:'; app.NumSignalsEditField = uieditfield(app.SignalConfigPanel, 'numeric'); app.NumSignalsEditField.Position = [110 190 60 22]; app.NumSignalsEditField.Value = 0; app.NumSignalsEditField.ValueChangedFcn = createCallbackFcn(app, @NumSignalsEditFieldValueChanged, true); % 信号类型控件 app.SignalTypeLabel = uilabel(app.SignalConfigPanel); app.SignalTypeLabel.Position = [20 160 80 22]; app.SignalTypeLabel.Text = '信号类型:'; app.SignalTypeDropDown = uidropdown(app.SignalConfigPanel); app.SignalTypeDropDown.Position = [110 160 80 22]; app.SignalTypeDropDown.Items = {'正弦波', '方波', '随机噪声', '锯齿波', '脉冲信号'}; % 基带频率控件 app.FrequencyLabel = uilabel(app.SignalConfigPanel); app.FrequencyLabel.Position = [20 130 80 22]; app.FrequencyLabel.Text = '基带频率 (Hz):'; app.FrequencyEditField = uieditfield(app.SignalConfigPanel, 'numeric'); app.FrequencyEditField.Position = [110 130 60 22]; app.FrequencyEditField.Value = 50; % 载波频率控件 app.CarrierFrequencyLabel = uilabel(app.SignalConfigPanel); app.CarrierFrequencyLabel.Position = [210 130 100 22]; app.CarrierFrequencyLabel.Text = '载波频率 (Hz):'; app.CarrierFrequencyEditField = uieditfield(app.SignalConfigPanel, 'numeric'); app.CarrierFrequencyEditField.Position = [300 130 60 22]; app.CarrierFrequencyEditField.Value = 1000; % 默认值 % 添加/移除按钮 app.AddSignalButton = uibutton(app.SignalConfigPanel, 'push'); app.AddSignalButton.ButtonPushedFcn = createCallbackFcn(app, @AddSignalButtonPushed, true); app.AddSignalButton.Position = [210 160 80 22]; app.AddSignalButton.Text = '添加信号'; app.RemoveSignalButton = uibutton(app.SignalConfigPanel, 'push'); app.RemoveSignalButton.ButtonPushedFcn = createCallbackFcn(app, @RemoveSignalButtonPushed, true); app.RemoveSignalButton.Position = [300 160 80 22]; app.RemoveSignalButton.Text = '移除信号'; % 信号列表 app.SignalsListLabel = uilabel(app.SignalConfigPanel); app.SignalsListLabel.Position = [20 100 80 22]; app.SignalsListLabel.Text = '信号列表:'; app.SignalsListBox = uilistbox(app.SignalConfigPanel); app.SignalsListBox.Position = [20 5 370 90]; app.SignalsListBox.Items = {}; % ==== 策略配置面板 ==== app.StrategyPanel = uipanel(app.ControlPanel); app.StrategyPanel.Title = '时隙分配策略'; app.StrategyPanel.Position = [20 425 410 150]; % 策略按钮组 app.StrategyButtonGroup = uibuttongroup(app.StrategyPanel); app.StrategyButtonGroup.SelectionChangedFcn = createCallbackFcn(app, @StrategyButtonGroupSelectionChanged, true); app.StrategyButtonGroup.Position = [20 40 200 80]; app.StrategyButtonGroup.Title = '选择策略'; % 单选按钮 app.FixedRadioButton = uiradiobutton(app.StrategyButtonGroup); app.FixedRadioButton.Text = '固定分配'; app.FixedRadioButton.Position = [11 35 75 22]; app.PriorityRadioButton = uiradiobutton(app.StrategyButtonGroup); app.PriorityRadioButton.Text = '优先级分配'; app.PriorityRadioButton.Position = [11 10 100 22]; app.PriorityRadioButton.Value = true; % 总时隙数 app.TotalSlotsLabel = uilabel(app.StrategyPanel); app.TotalSlotsLabel.Position = [240 30 80 22]; app.TotalSlotsLabel.Text = '总时隙数:'; app.TotalSlotsEditField = uieditfield(app.StrategyPanel, 'numeric'); app.TotalSlotsEditField.ValueChangedFcn = createCallbackFcn(app, @TotalSlotsEditFieldValueChanged, true); app.TotalSlotsEditField.Position = [300 30 60 22]; app.TotalSlotsEditField.Value = 50; % ==== 编码配置面板 ==== app.EncodingPanel = uipanel(app.ControlPanel); app.EncodingPanel.Title = '编码配置'; app.EncodingPanel.Position = [20 300 410 120]; app.EncodingTypeLabel = uilabel(app.EncodingPanel); app.EncodingTypeLabel.Position = [20 75 80 22]; app.EncodingTypeLabel.Text = '编码类型:'; app.EncodingTypeDropDown = uidropdown(app.EncodingPanel); app.EncodingTypeDropDown.Position = [110 75 100 22]; app.EncodingTypeDropDown.Items = {'PCM', 'DPCM', 'DM'}; app.EncodingTypeDropDown.Value = 'PCM'; app.EncodingTypeDropDown.ValueChangedFcn = createCallbackFcn(app, @EncodingTypeDropDownValueChanged, true); app.QuantBitsLabel = uilabel(app.EncodingPanel); app.QuantBitsLabel.Position = [20 45 80 22]; app.QuantBitsLabel.Text = '量化位数:'; app.QuantBitsEditField = uieditfield(app.EncodingPanel, 'numeric'); app.QuantBitsEditField.Position = [110 45 60 22]; app.QuantBitsEditField.Value = 8; app.QuantBitsEditField.ValueChangedFcn = createCallbackFcn(app, @QuantBitsEditFieldValueChanged, true); app.StepSizeLabel = uilabel(app.EncodingPanel); app.StepSizeLabel.Position = [20 15 80 22]; app.StepSizeLabel.Text = '步长:'; app.StepSizeEditField = uieditfield(app.EncodingPanel, 'numeric'); app.StepSizeEditField.Position = [110 15 60 22]; app.StepSizeEditField.Value = 0.05; app.StepSizeEditField.ValueChangedFcn = createCallbackFcn(app, @StepSizeEditFieldValueChanged, true); % ==== 调制配置面板 ==== app.ModulationPanel = uipanel(app.ControlPanel); app.ModulationPanel.Title = '调制配置'; app.ModulationPanel.Position = [20 225 410 70]; app.ModulationTypeLabel = uilabel(app.ModulationPanel); app.ModulationTypeLabel.Position = [20 15 80 22]; app.ModulationTypeLabel.Text = '调制方式:'; app.ModulationTypeDropDown = uidropdown(app.ModulationPanel); app.ModulationTypeDropDown.Position = [110 15 100 22]; app.ModulationTypeDropDown.Items = {'None', 'BPSK', 'QPSK', '16QAM'}; app.ModulationTypeDropDown.Value = 'BPSK'; app.ModulationTypeDropDown.ValueChangedFcn = createCallbackFcn(app, @ModulationTypeDropDownValueChanged, true); % ==== 帧配置面板 ==== app.FrameConfigPanel = uipanel(app.ControlPanel); app.FrameConfigPanel.Title = '帧配置'; app.FrameConfigPanel.Position = [20 150 410 70]; app.FrameHeaderLabel = uilabel(app.FrameConfigPanel); app.FrameHeaderLabel.Position = [20 15 80 22]; app.FrameHeaderLabel.Text = '帧头:'; app.FrameHeaderEditField = uieditfield(app.FrameConfigPanel, 'text'); app.FrameHeaderEditField.Position = [110 15 100 22]; app.FrameHeaderEditField.Value = 'A5A5'; app.FrameHeaderEditField.ValueChangedFcn = createCallbackFcn(app, @FrameHeaderEditFieldValueChanged, true); app.CRCCheckBox = uicheckbox(app.FrameConfigPanel); app.CRCCheckBox.Text = '启用CRC校验'; app.CRCCheckBox.Position = [220 15 120 22]; app.CRCCheckBox.Value = true; app.CRCCheckBox.ValueChangedFcn = createCallbackFcn(app, @CRCCheckBoxValueChanged, true); % ==== 仿真控制面板 ==== app.SimulationPanel = uipanel(app.ControlPanel); app.SimulationPanel.Title = '仿真控制'; app.SimulationPanel.Position = [20 5 410 140]; % 参数输入 app.SamplingRateLabel = uilabel(app.SimulationPanel); app.SamplingRateLabel.Position = [20 95 80 22]; app.SamplingRateLabel.Text = '采样率 (Hz):'; app.SamplingRateEditField = uieditfield(app.SimulationPanel, 'numeric'); app.SamplingRateEditField.ValueChangedFcn = createCallbackFcn(app, @SamplingRateEditFieldValueChanged, true); app.SamplingRateEditField.Position = [110 95 60 22]; app.SamplingRateEditField.Value = 1000; app.DurationLabel = uilabel(app.SimulationPanel); app.DurationLabel.Position = [20 65 80 22]; app.DurationLabel.Text = '持续时间 (s):'; app.DurationEditField = uieditfield(app.SimulationPanel, 'numeric'); app.DurationEditField.ValueChangedFcn = createCallbackFcn(app, @DurationEditFieldValueChanged, true); app.DurationEditField.Position = [110 65 60 22]; app.DurationEditField.Value = 1; app.SNRLabel = uilabel(app.SimulationPanel); app.SNRLabel.Position = [20 35 80 22]; app.SNRLabel.Text = '信噪比 (dB):'; app.SNREditField = uieditfield(app.SimulationPanel, 'numeric'); app.SNREditField.ValueChangedFcn = createCallbackFcn(app, @SNREditFieldValueChanged, true); app.SNREditField.Position = [110 35 60 22]; app.SNREditField.Value = 20; app.IterationsLabel = uilabel(app.SimulationPanel); app.IterationsLabel.Position = [200 95 80 22]; app.IterationsLabel.Text = '迭代次数:'; app.IterationsEditField = uieditfield(app.SimulationPanel, 'numeric'); app.IterationsEditField.ValueChangedFcn = createCallbackFcn(app, @IterationsEditFieldValueChanged, true); app.IterationsEditField.Position = [290 95 60 22]; app.IterationsEditField.Value = 5; % 状态显示 app.StatusLabel = uilabel(app.SimulationPanel); app.StatusLabel.HorizontalAlignment = 'center'; app.StatusLabel.Position = [20 5 100 22]; app.StatusLabel.Text = '准备就绪'; app.ProgressBar = uilamp(app.SimulationPanel); app.ProgressBar.Position = [150 5 110 20]; app.ProgressBar.Color = [0.47 0.67 0.19]; % 仿真按钮 app.RunSimulationButton = uibutton(app.SimulationPanel, 'push'); app.RunSimulationButton.ButtonPushedFcn = createCallbackFcn(app, @RunSimulationButtonPushed, true); app.RunSimulationButton.Position = [200 20 80 30]; app.RunSimulationButton.Text = '基础仿真'; app.RunFeedbackButton = uibutton(app.SimulationPanel, 'push'); app.RunFeedbackButton.ButtonPushedFcn = createCallbackFcn(app, @RunFeedbackButtonPushed, true); app.RunFeedbackButton.Position = [290 20 80 30]; app.RunFeedbackButton.Text = '反馈控制'; app.RunAnalysisButton = uibutton(app.SimulationPanel, 'push'); app.RunAnalysisButton.ButtonPushedFcn = createCallbackFcn(app, @RunAnalysisButtonPushed, true); app.RunAnalysisButton.Position = [200 60 170 30]; app.RunAnalysisButton.Text = '性能分析'; % ==== 结果显示区域 ==== app.ResultsTabGroup = uitabgroup(app.UIFigure); app.ResultsTabGroup.Position = [480 20 900 850]; % 信号可视化标签页 app.SignalsTab = uitab(app.ResultsTabGroup); app.SignalsTab.Title = '信号可视化'; % 创建2x2网格布局 grid = uigridlayout(app.SignalsTab); grid.RowHeight = {'1x', '1x'}; grid.ColumnWidth = {'1x', '1x'}; grid.Padding = [10 10 10 10]; grid.RowSpacing = 10; grid.ColumnSpacing = 10; % 原始信号坐标轴 app.OriginalAxes = uiaxes(grid); title(app.OriginalAxes, '原始信号'); xlabel(app.OriginalAxes, '时间 (s)'); ylabel(app.OriginalAxes, '幅度'); app.OriginalAxes.Layout.Row = 1; app.OriginalAxes.Layout.Column = 1; % TDM信号坐标轴 app.TDMAxes = uiaxes(grid); title(app.TDMAxes, 'TDM复用信号'); xlabel(app.TDMAxes, '时间 (s)'); ylabel(app.TDMAxes, '幅度'); app.TDMAxes.Layout.Row = 1; app.TDMAxes.Layout.Column = 2; % 解复用信号坐标轴 app.DemuxAxes = uiaxes(grid); title(app.DemuxAxes, '解复用信号'); xlabel(app.DemuxAxes, '时间 (s)'); ylabel(app.DemuxAxes, '幅度'); app.DemuxAxes.Layout.Row = 2; app.DemuxAxes.Layout.Column = 1; % 同步后信号坐标轴 app.SyncedAxes = uiaxes(grid); title(app.SyncedAxes, '同步后信号'); xlabel(app.SyncedAxes, '时间 (s)'); ylabel(app.SyncedAxes, '幅度'); app.SyncedAxes.Layout.Row = 2; app.SyncedAxes.Layout.Column = 2; % 性能分析标签页 app.PerformanceTab = uitab(app.ResultsTabGroup); app.PerformanceTab.Title = '性能分析'; % BER坐标轴 app.BERAxes = uiaxes(app.PerformanceTab); title(app.BERAxes, '信号质量分析'); xlabel(app.BERAxes, '信号编号'); app.BERAxes.Position = [100 200 700 500]; yyaxis(app.BERAxes, 'left'); ylabel(app.BERAxes, 'BER'); yyaxis(app.BERAxes, 'right'); ylabel(app.BERAxes, 'SNR (dB)'); % 系统参数标签页 app.ParametersTab = uitab(app.ResultsTabGroup); app.ParametersTab.Title = '系统参数'; % 参数文本区域 app.ParametersTextArea = uitextarea(app.ParametersTab); app.ParametersTextArea.Position = [20 20 860 800]; app.ParametersTextArea.Value = {'系统参数将在此显示'}; end end methods (Access = public) % Construct app function app = TDMApp8888 % Create and configure 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 % 辅助函数 function out = ifelse(condition, true_val, false_val) if condition out = true_val; else out = false_val; end end 第二段:classdef TDMSystem properties params % 系统参数 originalSignals % 原始信号 encodedSignals % 编码后信号(二进制位流) modulatedSignals % 调制后信号 demodulatedSignals % 解调后信号 decodedSignals % 解码后信号 tdmSignal % TDM复用信号 receivedSignal % 接收信号 demuxSignals % 解复用信号 syncedSignals % 同步后信号 signalInfo % 信号信息 allocatedSlots % 分配的时隙 slotAssignment % 时隙分配矩阵 performance % 性能指标 encodingParams % 编码参数 modulationParams % 调制参数 frameConfig % 帧配置 quantizationError % 量化误差 frameHeaders % 帧头位置 crcErrors % CRC错误统计 paddingInfo % 填充信息 debugInfo % 调试信息 end methods function obj = TDMSystem(params) % 参数验证和默认值设置 if ~isstruct(params) error('输入参数必须是结构体'); end % 必需参数检查 requiredFields = {'fs', 'duration', 'numSignals', 'snrDb', ... 'totalSlots', 'strategy', 'signalModulations'}; for i = 1:length(requiredFields) if ~isfield(params, requiredFields{i}) error('缺少必需的参数字段: %s', requiredFields{i}); end end % 设置默认值 if ~isfield(params, 'clockDrift') params.clockDrift = zeros(1, params.numSignals); end if ~isfield(params, 'priorities') params.priorities = ones(1, params.numSignals); end if ~isfield(params, 'frameHeader') params.frameHeader = 'A5A5'; end if ~isfield(params, 'useCRC') params.useCRC = true; end if ~isfield(params, 'encodingType') params.encodingType = 'PCM'; end if ~isfield(params, 'quantBits') && strcmp(params.encodingType, 'PCM') params.quantBits = 8; end if ~isfield(params, 'stepSize') && contains(params.encodingType, {'DPCM', 'DM'}) params.stepSize = 0.05; end % 归一化优先级权重 params.priorities = params.priorities / sum(params.priorities); obj.params = params; % 计算关键参数 totalSamples = params.fs * params.duration; obj.params.slotSamples = floor(totalSamples / params.totalSlots); % 初始化性能指标 obj.performance = struct(); obj.quantizationError = zeros(params.numSignals, 1); obj.crcErrors = struct('total', 0, 'perSignal', zeros(1, params.numSignals)); obj.paddingInfo = struct('encoded', zeros(1, params.numSignals)); obj.debugInfo = struct('signalLengths', []); % 初始化编码参数 obj.encodingParams = struct(... 'type', params.encodingType, ... 'quantBits', ifelse(isfield(params, 'quantBits'), params.quantBits, 8), ... 'stepSize', ifelse(isfield(params, 'stepSize'), params.stepSize, 0.05)); % 初始化调制参数 obj.modulationParams = struct(... 'type', ifelse(isfield(params, 'modulationType'), params.modulationType, 'BPSK'), ... 'signalModulations', {params.signalModulations}); % 初始化帧配置 obj.frameConfig = struct(... 'header', params.frameHeader, ... 'useCRC', params.useCRC); end function obj = runSimulation(obj) try % 处理流程 obj = obj.encodeSignals(); % 信号量化与编码 obj = obj.modulateSignals(); % 信号调制 obj = obj.allocateSlots(); % 时隙分配 obj = obj.multiplex(); % TDM复用 obj = obj.transmit(); % 信道传输 obj = obj.frameSynchronization(); % 帧同步 obj = obj.demultiplex(); % 解复用 obj = obj.demodulateSignals(); % 信号解调 obj = obj.decodeSignals(); % 信号解码 obj = obj.synchronize(); % 信号同步 obj = obj.evaluatePerformance(); % 性能评估 catch ME rethrow(ME); end end function obj = encodeSignals(obj) obj.encodedSignals = cell(1, obj.params.numSignals); for i = 1:obj.params.numSignals signal = obj.originalSignals(i, :); % 独立量化与编码处理 switch obj.encodingParams.type case 'PCM' [quantized, quantErr] = obj.pcm_encode(signal, obj.encodingParams.quantBits); [binStream, ~] = obj.int_to_bin(quantized, obj.encodingParams.quantBits); obj.quantizationError(i) = mean(quantErr.^2); case 'DPCM' [quantized, quantErr] = obj.dpcm_encode(signal, obj.encodingParams.quantBits, obj.encodingParams.stepSize); [binStream, ~] = obj.int_to_bin(quantized, obj.encodingParams.quantBits); obj.quantizationError(i) = mean(quantErr.^2); case 'DM' binStream = obj.dm_encode(signal, obj.encodingParams.stepSize); otherwise error('未知编码类型: %s', obj.encodingParams.type); end % 根据调制方式预填充位流 modType = obj.modulationParams.signalModulations{i}; bitsPerSymbol = obj.getBitsPerSymbol(modType); % 计算填充位数 paddingBits = mod(length(binStream), bitsPerSymbol); if paddingBits > 0 padding = bitsPerSymbol - paddingBits; binStream = [binStream, false(1, padding)]; % 填充0 else padding = 0; end obj.paddingInfo.encoded(i) = padding; % 统一转换为逻辑数组 if ~islogical(binStream) binStream = logical(binStream); end obj.encodedSignals{i} = binStream; end end function obj = modulateSignals(obj) obj.modulatedSignals = cell(obj.params.numSignals, 1); for i = 1:obj.params.numSignals binStream = obj.encodedSignals{i}; modulationType = obj.modulationParams.signalModulations{i}; % 独立调制处理 switch modulationType case 'None' obj.modulatedSignals{i} = binStream; case 'BPSK' obj.modulatedSignals{i} = 2*double(binStream) - 1; case 'QPSK' % 确保长度是2的倍数 if mod(length(binStream), 2) ~= 0 binStream = [binStream, false]; % 填充1位 end symbols = reshape(binStream, 2, [])'; I = 2*double(symbols(:,1)) - 1; Q = 2*double(symbols(:,2)) - 1; obj.modulatedSignals{i} = (I + 1i*Q)/sqrt(2); case '16QAM' % 确保长度是4的倍数 if mod(length(binStream), 4) ~= 0 padding = 4 - mod(length(binStream), 4); binStream = [binStream, false(1, padding)]; end symbols = reshape(binStream, 4, [])'; I = 2*double(symbols(:,1)) + double(symbols(:,2)); Q = 2*double(symbols(:,3)) + double(symbols(:,4)); % 映射到星座点 I(I==0) = -3; I(I==1) = -1; I(I==2) = 1; I(I==3) = 3; Q(Q==0) = -3; Q(Q==1) = -1; Q(Q==2) = 1; Q(Q==3) = 3; obj.modulatedSignals{i} = (I + 1i*Q)/sqrt(10); end end end function obj = allocateSlots(obj) if obj.params.totalSlots < obj.params.numSignals error('总时隙数(%d)小于信号数(%d)', ... obj.params.totalSlots, obj.params.numSignals); end obj.allocatedSlots = zeros(1, obj.params.numSignals); obj.slotAssignment = zeros(obj.params.numSignals, obj.params.totalSlots); % 时隙分配策略 switch obj.params.strategy case 'fixed' baseSlots = floor(obj.params.totalSlots / obj.params.numSignals); obj.allocatedSlots = baseSlots * ones(1, obj.params.numSignals); remainder = obj.params.totalSlots - sum(obj.allocatedSlots); [~, sortedIdx] = sort(obj.params.priorities, 'descend'); for i = 1:remainder if i <= obj.params.numSignals obj.allocatedSlots(sortedIdx(i)) = obj.allocatedSlots(sortedIdx(i)) + 1; end end case 'priority' minSlots = ones(1, obj.params.numSignals); availableSlots = obj.params.totalSlots - sum(minSlots); if availableSlots < 0 error('总时隙数(%d)不足以分配', obj.params.totalSlots); end weights = obj.params.priorities / sum(obj.params.priorities); extraSlots = floor(availableSlots * weights); obj.allocatedSlots = minSlots + extraSlots; remainder = availableSlots - sum(extraSlots); [~, idx] = sort(obj.params.priorities, 'descend'); for i = 1:remainder obj.allocatedSlots(idx(i)) = obj.allocatedSlots(idx(i)) + 1; end end % 构建时隙分配矩阵 current_slot = 1; for i = 1:obj.params.numSignals slotsToAssign = obj.allocatedSlots(i); for j = 1:slotsToAssign if current_slot > obj.params.totalSlots break; end obj.slotAssignment(i, current_slot) = 1; current_slot = current_slot + 1; end end end function obj = multiplex(obj) totalSamples = obj.params.fs * obj.params.duration; headerBin = obj.str2bin(obj.frameConfig.header); headerLen = length(headerBin); slotSamples = obj.params.slotSamples; dataSizePerFrame = slotSamples * obj.params.totalSlots; frameSizeTotal = dataSizePerFrame + headerLen; numFrames = ceil(totalSamples / dataSizePerFrame); % 初始化TDM信号 obj.tdmSignal = complex(zeros(1, numFrames * frameSizeTotal)); obj.frameHeaders = zeros(1, numFrames); for frameIdx = 1:numFrames frameStartTotal = (frameIdx-1) * frameSizeTotal + 1; headerEnd = frameStartTotal + headerLen - 1; % 添加帧头 obj.tdmSignal(frameStartTotal:headerEnd) = complex(headerBin, 0); obj.frameHeaders(frameIdx) = frameStartTotal; dataStart = headerEnd + 1; % 时隙分配与信号插入 for slot = 1:obj.params.totalSlots slotStart = dataStart + (slot-1) * slotSamples; slotEnd = slotStart + slotSamples - 1; if slotEnd > length(obj.tdmSignal) break; end % 获取当前时隙对应的信号索引 signalIdx = find(obj.slotAssignment(:, slot) == 1, 1); if ~isempty(signalIdx) % 计算信号位置 signalStart = (frameIdx-1) * dataSizePerFrame + (slot-1) * slotSamples + 1; signalEnd = min(signalStart + slotSamples - 1, length(obj.modulatedSignals{signalIdx})); % 获取信号片段 signalSegment = obj.modulatedSignals{signalIdx}(signalStart:signalEnd); % 长度匹配处理 if length(signalSegment) < slotSamples padding = complex(zeros(1, slotSamples - length(signalSegment))); signalSegment = [signalSegment, padding]; elseif length(signalSegment) > slotSamples signalSegment = signalSegment(1:slotSamples); end % 插入时隙 obj.tdmSignal(slotStart:slotEnd) = signalSegment; else obj.tdmSignal(slotStart:slotEnd) = complex(zeros(1, slotSamples)); end end end end function obj = transmit(obj) signal_power = mean(abs(obj.tdmSignal).^2); if signal_power == 0 signal_power = 1e-10; end linear_snr = 10^(obj.params.snrDb/10); noise_power = signal_power / linear_snr; if ~isreal(obj.tdmSignal) noise = sqrt(noise_power/2) * (randn(size(obj.tdmSignal)) + 1i*randn(size(obj.tdmSignal))); else noise = sqrt(noise_power) * randn(size(obj.tdmSignal)); end obj.receivedSignal = obj.tdmSignal + noise; end function obj = frameSynchronization(obj) headerBin = obj.str2bin(obj.frameConfig.header); headerLen = length(headerBin); slotSamples = obj.params.slotSamples; dataSizePerFrame = slotSamples * obj.params.totalSlots; frameSizeTotal = dataSizePerFrame + headerLen; if isreal(obj.receivedSignal) headerRef = complex(headerBin, 0); else headerRef = complex(headerBin, zeros(size(headerBin))); end correlation = conv(obj.receivedSignal, conj(fliplr(headerRef)), 'same'); correlationMag = abs(correlation); [~, locs] = findpeaks(correlationMag, 'MinPeakHeight', 0.7*max(correlationMag), ... 'MinPeakDistance', round(frameSizeTotal*0.8)); detectedHeaders = []; for i = 1:length(locs) if i == 1 || (locs(i) - locs(i-1)) > 0.8 * frameSizeTotal detectedHeaders = [detectedHeaders, locs(i)]; end end obj.frameHeaders = detectedHeaders; end function obj = demultiplex(obj) totalSamples = obj.params.fs * obj.params.duration; headerBin = obj.str2bin(obj.frameConfig.header); headerLen = length(headerBin); slotSamples = obj.params.slotSamples; dataSizePerFrame = slotSamples * obj.params.totalSlots; obj.demuxSignals = cell(1, obj.params.numSignals); for i = 1:obj.params.numSignals obj.demuxSignals{i} = complex(zeros(1, totalSamples)); end for frameIdx = 1:length(obj.frameHeaders) headerStart = obj.frameHeaders(frameIdx); if headerStart + headerLen + dataSizePerFrame - 1 > length(obj.receivedSignal) continue; end frameData = obj.receivedSignal(headerStart+headerLen:headerStart+headerLen+dataSizePerFrame-1); for slot = 1:obj.params.totalSlots slotStart = (slot-1)*slotSamples + 1; slotEnd = slotStart + slotSamples - 1; if slotEnd > length(frameData) continue; end slotData = frameData(slotStart:slotEnd); signalIdx = find(obj.slotAssignment(:, slot) == 1, 1); if ~isempty(signalIdx) % 计算信号位置 signalStart = (frameIdx-1)*dataSizePerFrame + (slot-1)*slotSamples + 1; signalEnd = signalStart + length(slotData) - 1; % 确保不越界 if signalEnd > totalSamples signalEnd = totalSamples; slotData = slotData(1:(signalEnd - signalStart + 1)); end % 存储解复用数据 obj.demuxSignals{signalIdx}(signalStart:signalEnd) = slotData; end end end end function obj = demodulateSignals(obj) totalSamples = size(obj.demuxSignals{1}, 2); obj.demodulatedSignals = false(obj.params.numSignals, totalSamples); for i = 1:obj.params.numSignals modSignal = obj.demuxSignals{i}; modulationType = obj.modulationParams.signalModulations{i}; switch modulationType case 'None' bits = real(modSignal) > 0.5; case 'BPSK' bits = real(modSignal) > 0; case 'QPSK' I = real(modSignal) > 0; Q = imag(modSignal) > 0; bits = [I, Q]'; bits = bits(:)'; case '16QAM' symbols = modSignal * sqrt(10); I = real(symbols); Q = imag(symbols); bitsI1 = I > 0; bitsI2 = abs(I) > 2; bitsQ1 = Q > 0; bitsQ2 = abs(Q) > 2; bits = [bitsI1, bitsI2, bitsQ1, bitsQ2]'; bits = bits(:)'; end % 移除填充位 padding = obj.paddingInfo.encoded(i); if padding > 0 bits = bits(1:end-padding); end % 确保输出长度匹配 endIdx = min(length(bits), totalSamples); obj.demodulatedSignals(i, 1:endIdx) = bits(1:endIdx); end end function obj = decodeSignals(obj) totalSamples = size(obj.demodulatedSignals, 2); obj.decodedSignals = zeros(obj.params.numSignals, totalSamples); for i = 1:obj.params.numSignals binStream = obj.demodulatedSignals(i, :); switch obj.encodingParams.type case 'PCM' quantized = obj.bin_to_int(binStream, obj.encodingParams.quantBits); decoded = obj.pcm_decode(quantized, obj.encodingParams.quantBits); obj.decodedSignals(i, 1:length(decoded)) = decoded; case 'DPCM' quantized = obj.bin_to_int(binStream, obj.encodingParams.quantBits); decoded = obj.dpcm_decode(quantized, obj.encodingParams.quantBits, obj.encodingParams.stepSize); obj.decodedSignals(i, 1:length(decoded)) = decoded; case 'DM' decoded = obj.dm_decode(binStream, obj.encodingParams.stepSize); obj.decodedSignals(i, 1:length(decoded)) = decoded; end end end function obj = synchronize(obj) totalSamples = obj.params.fs * obj.params.duration; t = linspace(0, obj.params.duration, totalSamples); obj.syncedSignals = zeros(obj.params.numSignals, totalSamples); for i = 1:obj.params.numSignals decodedSignal = obj.decodedSignals(i, :); validIdx = find(~isnan(decodedSignal) & ~isinf(decodedSignal)); if isempty(validIdx) obj.syncedSignals(i, :) = 0; continue; end drift = obj.params.clockDrift(i); shifted_t = t * (1 + drift); % 确保时间向量不超出范围 max_time = max(shifted_t); if max_time > obj.params.duration shifted_t = shifted_t(shifted_t <= obj.params.duration); if numel(shifted_t) < totalSamples shifted_t = [shifted_t, linspace(shifted_t(end), obj.params.duration, ... totalSamples - numel(shifted_t))]; end end obj.syncedSignals(i, :) = interp1(... t(validIdx), decodedSignal(validIdx), shifted_t, 'pchip', 0); end end function obj = evaluatePerformance(obj) obj.performance = struct(... 'snr', zeros(1, obj.params.numSignals), ... 'ber', zeros(1, obj.params.numSignals), ... 'mse', zeros(1, obj.params.numSignals)); for i = 1:obj.params.numSignals orig_signal = obj.originalSignals(i, :); synced_signal = obj.syncedSignals(i, :); minLen = min(length(orig_signal), length(synced_signal)); if minLen == 0 continue; end orig_segment = orig_signal(1:minLen); synced_segment = synced_signal(1:minLen); % 计算MSE mse = mean((orig_segment - synced_segment).^2); obj.performance.mse(i) = mse; % 计算SNR signal_power = mean(orig_segment.^2); if mse > 0 obj.performance.snr(i) = 10 * log10(signal_power / mse); end % 计算BER modType = obj.modulationParams.signalModulations{i}; if any(strcmp(modType, {'BPSK', 'QPSK', '16QAM'})) orig_bits = obj.encodedSignals{i}; demod_bits = obj.demodulatedSignals(i, :); minLenBits = min(length(orig_bits), length(demod_bits)); if minLenBits > 0 orig_seg = orig_bits(1:minLenBits); demod_seg = demod_bits(1:minLenBits); bit_errors = sum(orig_seg ~= demod_seg); obj.performance.ber(i) = bit_errors / minLenBits; end end end end % ====== 辅助方法 ====== function [binStream, padding] = int_to_bin(~, values, numBits) numValues = numel(values); totalBits = numValues * numBits; binStream = false(1, totalBits); for i = 1:numValues val = uint32(values(i)); startIdx = (i-1)*numBits + 1; for bitPos = 1:numBits bit = bitget(val, bitPos, 'uint32'); binStream(startIdx + numBits - bitPos) = (bit == 1); end end padding = 0; end function values = bin_to_int(~, binStream, numBits) numValues = floor(length(binStream) / numBits); padding = length(binStream) - numValues * numBits; if padding > 0 binStream = binStream(1:end-padding); end values = zeros(1, numValues); for i = 1:numValues startIdx = (i-1)*numBits + 1; endIdx = startIdx + numBits - 1; segment = binStream(startIdx:endIdx); val = 0; for j = 1:numBits val = val * 2 + segment(j); end values(i) = val; end end function bin = str2bin(~, str) dec = uint8(str); binStr = dec2bin(dec, 8)'; bin = double(binStr(:)') - 48; end function bits = getBitsPerSymbol(~, modType) switch modType case {'BPSK', 'None'} bits = 1; case 'QPSK' bits = 2; case '16QAM' bits = 4; otherwise bits = 1; end end % ====== 编码核心方法 ====== function [quantized, quantErr] = pcm_encode(~, signal, quantBits) maxVal = max(abs(signal)); quantLevels = 2^quantBits; quantStep = 2*maxVal/(quantLevels-1); minVal = -maxVal; quantized = round((signal - minVal)/quantStep); quantized = max(0, min(quantized, quantLevels-1)); quantErr = signal - (quantized*quantStep + minVal); end function decoded = pcm_decode(~, quantized, quantBits) maxVal = max(quantized) * (2^quantBits-1)/2^quantBits; minVal = -maxVal; quantStep = 2*maxVal/(2^quantBits-1); decoded = quantized*quantStep + minVal; end function [encoded, quantErr] = dpcm_encode(obj, signal, quantBits, stepSize) n = length(signal); encoded = zeros(1, n); quantErr = zeros(1, n); predictor = 0; for i = 1:n error = signal(i) - predictor; quantizedError = obj.quantize(error, quantBits, stepSize); encoded(i) = quantizedError; predictor = predictor + quantizedError; quantErr(i) = error - quantizedError; end end function decoded = dpcm_decode(~, encoded, ~, ~) n = length(encoded); decoded = zeros(1, n); predictor = 0; for i = 1:n decoded(i) = predictor + encoded(i); predictor = decoded(i); end end function encoded = dm_encode(~, signal, stepSize) n = length(signal); encoded = false(1, n); integrator = 0; for i = 1:n if signal(i) > integrator encoded(i) = true; integrator = integrator + stepSize; else encoded(i) = false; integrator = integrator - stepSize; end end end function decoded = dm_decode(~, encoded, stepSize) n = length(encoded); decoded = zeros(1, n); integrator = 0; for i = 1:n if encoded(i) integrator = integrator + stepSize; else integrator = integrator - stepSize; end decoded(i) = integrator; end end function quantized = quantize(~, value, quantBits, stepSize) levels = 2^quantBits; maxVal = (levels-1) * stepSize / 2; clipped = max(min(value, maxVal), -maxVal); quantized = round(clipped/stepSize) * stepSize; end end end % 辅助函数 function out = ifelse(condition, true_val, false_val) if condition out = true_val; else out = false_val; end end
06-25
代码一的基础上帮我加入代码二的界面和功能。要求保留代码一的数据处理和图像显示,之加入代码二的界面和故障判断功能代码一如下% voltageDataProcessor_optimized.m - 优化的电压数据实时处理与绘图程序 function voltageDataProcessor_optimized() % 选择输入文件 [fileName, filePath] = uigetfile('*.dat', '选择DAT文件'); if fileName == 0 fprintf('未选择文件,程序退出。\n'); return; end % 完整文件路径 fullPath = fullfile(filePath, fileName); % 创建图形窗口 fig = figure('Name', '电压数据实时处理与显示', ... 'Position', [100, 100, 1000, 800], ... 'CloseRequestFcn', @closeFigureCallback); % 创建4个子图 ax(1) = subplot(2, 2, 1); p1 = plot(ax(1), NaN, NaN, 'b'); title('电压检测值1'); xlabel('采样点'); ylabel('电压(V)'); grid on; ax(2) = subplot(2, 2, 2); p2 = plot(ax(2), NaN, NaN, 'g'); title('电压检测值2'); xlabel('采样点'); ylabel('电压(V)'); grid on; ax(3) = subplot(2, 2, 3); p3 = plot(ax(3), NaN, NaN, 'r'); title('电压检测值3'); xlabel('采样点'); ylabel('电压(V)'); grid on; ax(4) = subplot(2, 2, 4); p4 = plot(ax(4), NaN, NaN, 'm'); title('电压检测值4'); xlabel('采样点'); ylabel('电压(V)'); grid on; % 初始化数据存储变量 voltage1 = []; voltage2 = []; voltage3 = []; voltage4 = []; % 文件读取状态 fileID = -1; lastPos = 0; % 滤波参数设置 filterOrder = 4; % 滤波器阶数 cutoffFreq = 0.1; % 截止频率(归一化) % 创建低通滤波器 [b, a] = butter(filterOrder, cutoffFreq, 'low'); % 状态文本 statusText = uicontrol('Style', 'text', ... 'Position', [20, 20, 300, 20], ... 'String', '准备开始...', ... 'BackgroundColor', [0.9, 0.9, 0.9], ... 'HorizontalAlignment', 'left'); % 频率文本 freqText = uicontrol('Style', 'text', ... 'Position', [350, 20, 300, 20], ... 'String', '频率: 计算中...', ... 'BackgroundColor', [0.9, 0.9, 0.9], ... 'HorizontalAlignment', 'left'); % 停止按钮 stopBtn = uicontrol('Style', 'pushbutton', ... 'Position', [700, 20, 100, 30], ... 'String', '停止监控', ... 'Callback', @stopMonitoring, ... 'BackgroundColor', [0.9, 0.4, 0.4]); % 存储应用数据 appData = struct(... 'voltage1', voltage1, ... 'voltage2', voltage2, ... 'voltage3', voltage3, ... 'voltage4', voltage4, ... 'fileID', fileID, ... 'lastPos', lastPos, ... 'fullPath', fullPath, ... 'running', true, ... 'p1', p1, ... 'p2', p2, ... 'p3', p3, ... 'p4', p4, ... 'ax', ax, ... 'statusText', statusText, ... 'freqText', freqText, ... 'b', b, ... 'a', a, ... 'maxDataPoints', 10000, ... % 最大存储点数 'maxDisplayPoints', 1000 ... % 最大显示点数 ); setappdata(fig, 'appData', appData); % 打开文件 try fileID = fopen(fullPath, 'r'); if fileID == -1 error('无法打开文件: %s', fullPath); end % 更新应用数据 appData.fileID = fileID; setappdata(fig, 'appData', appData); set(statusText, 'String', '文件已打开,开始监控...'); catch ME set(statusText, 'String', ['错误: ' ME.message]); return; end % 启动定时器 timerObj = timer(... 'ExecutionMode', 'fixedRate', ... 'Period', 0.5, ... % 更新间隔0.5秒 'TimerFcn', {@updatePlots, fig}, ... 'ErrorFcn', @timerErrorCallback, ... 'BusyMode', 'drop' ... ); start(timerObj); % 存储定时器对象 appData.timerObj = timerObj; setappdata(fig, 'appData', appData); % 关闭回调函数 function closeFigureCallback(~, ~) appData = getappdata(fig, 'appData'); if isfield(appData, 'timerObj') && isvalid(appData.timerObj) stop(appData.timerObj); delete(appData.timerObj); end if isfield(appData, 'fileID') && appData.fileID > 2 fclose(appData.fileID); end delete(fig); end % 停止监控回调 function stopMonitoring(~, ~) appData = getappdata(fig, 'appData'); appData.running = false; setappdata(fig, 'appData', appData); set(statusText, 'String', '监控已停止'); if isfield(appData, 'timerObj') && isvalid(appData.timerObj) stop(appData.timerObj); end end % 定时器错误回调 function timerErrorCallback(~, event) set(statusText, 'String', ['定时器错误: ' event.Data.message]); end end % 更新图形函数 function updatePlots(~, ~, figHandle) if ~ishandle(figHandle) return; end appData = getappdata(figHandle, 'appData'); if ~appData.running return; end try % 获取当前文件大小 fseek(appData.fileID, 0, 'eof'); currentFileSize = ftell(appData.fileID); % 检查文件是否有新内容 if currentFileSize > appData.lastPos % 移动到上次结束位置 fseek(appData.fileID, appData.lastPos, 'bof'); % 读取新数据 newData = textscan(appData.fileID, '%s', 'Delimiter', '\n'); newLines = newData{1}; % 处理新数据 newVoltage1 = []; newVoltage2 = []; newVoltage3 = []; newVoltage4 = []; for i = 1:length(newLines) line = strtrim(newLines{i}); % 跳过空行 if isempty(line) continue; end % 提取数字 - 更健壮的方法 [num, success] = extractNumber(line); if success % 根据行号分配到对应的电压数组 switch mod(i-1, 4) + 1 case 1 newVoltage1 = [newVoltage1; num]; case 2 newVoltage2 = [newVoltage2; num]; case 3 newVoltage3 = [newVoltage3; num]; case 4 newVoltage4 = [newVoltage4; num]; end end end % 更新数据 if ~isempty(newVoltage1) appData.voltage1 = [appData.voltage1; newVoltage1]; appData.voltage2 = [appData.voltage2; newVoltage2]; appData.voltage3 = [appData.voltage3; newVoltage3]; appData.voltage4 = [appData.voltage4; newVoltage4]; % === 新增:限制数据缓冲区大小 === if length(appData.voltage1) > appData.maxDataPoints keepPoints = appData.maxDataPoints; appData.voltage1 = appData.voltage1(end-keepPoints+1:end); appData.voltage2 = appData.voltage2(end-keepPoints+1:end); appData.voltage3 = appData.voltage3(end-keepPoints+1:end); appData.voltage4 = appData.voltage4(end-keepPoints+1:end); end % ============================== % 更新图形 updatePlot(appData.p1, appData.voltage1, appData.maxDisplayPoints); updatePlot(appData.p2, appData.voltage2, appData.maxDisplayPoints); updatePlot(appData.p3, appData.voltage3, appData.maxDisplayPoints); updatePlot(appData.p4, appData.voltage4, appData.maxDisplayPoints); % 计算并显示通道4的频率 if length(appData.voltage4) > 100 fs = 100; % 假设采样频率为100Hz frequency = calculateFrequency(appData.voltage4, fs); set(appData.freqText, 'String', sprintf('频率: %.2f Hz', frequency)); title(appData.ax(4), sprintf('电压检测值4 (主要频率: %.2f Hz)', frequency)); end % 应用滤波并更新滤波线 if length(appData.voltage1) >= 10 updateFilteredLine(appData.ax(1), appData.voltage1, appData.b, appData.a, appData.maxDisplayPoints); updateFilteredLine(appData.ax(2), appData.voltage2, appData.b, appData.a, appData.maxDisplayPoints); updateFilteredLine(appData.ax(3), appData.voltage3, appData.b, appData.a, appData.maxDisplayPoints); updateFilteredLine(appData.ax(4), appData.voltage4, appData.b, appData.a, appData.maxDisplayPoints); end % 更新状态 set(appData.statusText, 'String', sprintf('已处理 %d 个新数据点', length(newVoltage1))); end % 更新文件位置 appData.lastPos = currentFileSize; setappdata(figHandle, 'appData', appData); end % 刷新图形 drawnow limitrate; catch ME set(appData.statusText, 'String', ['错误: ' ME.message]); appData.running = false; setappdata(figHandle, 'appData', appData); end end % 数字提取函数 function [num, success] = extractNumber(line) % 尝试多种方法提取数字 success = false; num = NaN; % 方法1: 使用正则表达式提取第一个数字 tokens = regexp(line, '[-+]?\d*\.?\d+', 'match'); if ~isempty(tokens) num = str2double(tokens{1}); if ~isnan(num) success = true; return; end end % 方法2: 使用sscanf提取 [num, count] = sscanf(line, '%f'); if count >= 1 num = num(1); success = true; return; end % 方法3: 尝试去掉非数字字符 cleanLine = regexprep(line, '[^0-9\.\-+]', ''); if ~isempty(cleanLine) num = str2double(cleanLine); if ~isnan(num) success = true; end end end % 更新绘图函数 (修改后) function updatePlot(plotHandle, data, maxDisplayPoints) if isempty(data) return; end totalPoints = length(data); % 确定显示的起始点和结束点 if totalPoints > maxDisplayPoints startIdx = totalPoints - maxDisplayPoints + 1; endIdx = totalPoints; dataToShow = data(startIdx:end); else startIdx = 1; endIdx = totalPoints; dataToShow = data; end % 更新图形数据 set(plotHandle, 'XData', startIdx:endIdx, 'YData', dataToShow); % 更新坐标轴范围 ax = get(plotHandle, 'Parent'); xlim(ax, [startIdx, endIdx]); % 动态X轴范围 % 自动调整Y轴范围 minY = min(dataToShow); maxY = max(dataToShow); range = max(0.1, maxY - minY); ylim(ax, [minY - 0.1*range, maxY + 0.1*range]); end % 更新滤波线函数 (修改后) function updateFilteredLine(ax, data, b, a, maxDisplayPoints) % 检查是否已存在滤波线 lines = findobj(ax, 'Type', 'line'); filteredLine = []; % 查找滤波线(红色虚线) for i = 1:length(lines) if strcmp(get(lines(i), 'LineStyle'), '--') && ... isequal(get(lines(i), 'Color'), [1, 0, 0]) filteredLine = lines(i); break; end end % 应用滤波 filteredData = filtfilt(b, a, data); totalPoints = length(data); % 确定显示的起始点和结束点 if totalPoints > maxDisplayPoints startIdx = totalPoints - maxDisplayPoints + 1; endIdx = totalPoints; dataToShow = filteredData(startIdx:end); else startIdx = 1; endIdx = totalPoints; dataToShow = filteredData; end if isempty(filteredLine) % 创建新的滤波线 hold(ax, 'on'); plot(ax, startIdx:endIdx, dataToShow, 'r--', 'LineWidth', 1.5); hold(ax, 'off'); % 添加图例 if isempty(legend(ax)) legend(ax, {'原始数据', '滤波后数据'}, 'Location', 'best'); end else % 更新现有滤波线 set(filteredLine, 'XData', startIdx:endIdx, 'YData', dataToShow); end end % 频率计算函数(改进版) function freq = calculateFrequency(data, fs) % 确保数据长度足够 if length(data) < 100 freq = 0; return; end % 去直流分量 data = data - mean(data); % 使用自相关法检测周期 [acf, lags] = xcorr(data, 'coeff'); acf = acf(lags >= 0); lags = lags(lags >= 0); % 寻找主峰位置(跳过第一个点) [peaks, locs] = findpeaks(acf(2:end)); if isempty(peaks) freq = 0; return; end % 找到最高峰 [~, idx] = max(peaks); peakIdx = locs(idx) + 1; % 补偿跳过的第一个点 % 计算周期和频率 period = lags(peakIdx) / fs; freq = 1 / period; % 验证频率在合理范围内 if freq > fs/2 || freq < 0.1 % 如果自相关法失败,使用FFT作为备选 n = length(data); fftResult = fft(data); P2 = abs(fftResult/n); P1 = P2(1:floor(n/2)+1); P1(2:end-1) = 2*P1(2:end-1); f = fs*(0:(n/2))/n; [~, idx] = max(P1(2:end)); % 跳过DC分量 freq = f(idx+1); end end 代码二如下:function VoltageMonitor() % 主函数 - 电压监测系统 (修复版) % 创建主界面 fig = figure('Name', '电压监测系统', 'Position', [100, 100, 1300, 800], ... 'NumberTitle', 'off', 'MenuBar', 'none', 'Resize', 'on', ... 'CloseRequestFcn', @closeFigureCallback, ... 'Color', [0.94, 0.94, 0.94]); % 初始化数据结构 data = struct(... 'running', false, ... 'voltageData', {cell(1,4)}, ... % 存储原始电压数据 'timeData', {cell(1,4)}, ... % 存储时间数据 'filterData', {cell(1,4)}, ... % 存储滤波后数据 'standards', [1.5, 1.5, 1.5, 1.5], ... % 各通道标准值 'tolerances', [1.0, 1.0, 1.0, 1.0], ... % 各通道容差 'fileID', -1, ... % 文件ID 'startPosition', 0, ... % 文件读取起始位置 'timer', [], ... % 定时器对象 'lastUpdate', now, ... % 最后更新时间 'startTime', now ... % 监测开始时间 ); % 存储所有句柄和应用数据 handles = struct(); setappdata(fig, 'data', data); setappdata(fig, 'handles', handles); % 创建绘图区域 (2x2布局) - 左侧区域 plotPanel = uipanel('Parent', fig, 'Title', '电压波形监测', ... 'Position', [0.02, 0.05, 0.65, 0.9], 'Units', 'normalized', ... 'BackgroundColor', 'white'); handles.ax1 = subplot(2,2,1, 'Parent', plotPanel); handles.ax2 = subplot(2,2,2, 'Parent', plotPanel); handles.ax3 = subplot(2,2,3, 'Parent', plotPanel); handles.ax4 = subplot(2,2,4, 'Parent', plotPanel); axesHandles = [handles.ax1, handles.ax2, handles.ax3, handles.ax4]; titles = {'通道1: 电压检测值 (滤波后)', '通道2: 电压检测值 (滤波后)', ... '通道3: 电压检测值 (滤波后)', '通道4: 电压检测值 (滤波后)'}; colors = {'b', 'r', 'g', 'm'}; % 不同通道的颜色 % 初始化绘图 for i = 1:4 handles.plotHandles(i) = plot(axesHandles(i), 0, 0, 'Color', colors{i}, 'LineWidth', 1.5); title(axesHandles(i), titles{i}, 'FontSize', 10); xlabel(axesHandles(i), '时间 (s)', 'FontSize', 8); ylabel(axesHandles(i), '电压 (V)', 'FontSize', 8); grid(axesHandles(i), 'on'); set(axesHandles(i), 'FontSize', 8); hold(axesHandles(i), 'on'); % 启用保持模式 end % 右侧控制面板 controlPanel = uipanel('Parent', fig, 'Title', '控制面板', ... 'Position', [0.68, 0.05, 0.3, 0.9], 'Units', 'normalized', ... 'BackgroundColor', [0.9, 0.9, 0.9]); % 文件选择区域 (顶部) handles.filePanel = uipanel('Parent', controlPanel, 'Title', '文件设置', ... 'Position', [0.05, 0.85, 0.9, 0.13], 'Units', 'normalized'); handles.filePathDisplay = uicontrol('Parent', handles.filePanel, 'Style', 'text', ... 'String', '未选择文件', 'Position', [10, 50, 350, 20], ... 'FontSize', 9, 'HorizontalAlignment', 'left', 'BackgroundColor', 'white'); % 修复:'Parent' 而不是 '极Parent' handles.selectBtn = uicontrol('Parent', handles.filePanel, 'Style', 'pushbutton', ... 'String', '选择DAT文件', 'Position', [10, 10, 100, 30], ... 'Callback', @selectFile, 'FontSize', 10); % 控制按钮区域 handles.controlPanel = uipanel('Parent', controlPanel, 'Title', '检测控制', ... 'Position', [0.05, 0.7, 0.9, 0.13], 'Units', 'normalized'); % 修复:'Parent' 而不是 '极Parent' handles.startBtn = uicontrol('Parent', handles.controlPanel, 'Style', 'pushbutton', ... 'String', '开始检测', 'Position', [20, 10, 100, 30], ... 'Callback', @toggleMonitoring, 'BackgroundColor', [0.4, 0.8, 0.4], 'FontSize', 10); % 修复:'Parent' 而不是 '极Parent' handles.stopBtn = uicontrol('Parent', handles.controlPanel, 'Style', 'pushbutton', ... 'String', '结束检测', 'Position', [150, 10, 100, 30], ... 'Callback', @stopMonitoring, 'BackgroundColor', [0.9, 0.4, 0.4], 'FontSize', 10); % 频率显示区域 handles.freqPanel = uipanel('Parent', controlPanel, 'Title', '通道4频率计算', ... 'Position', [0.05, 0.55, 0.9, 0.13], 'Units', 'normalized'); handles.freqDisplay = uicontrol('Parent', handles.freqPanel, 'Style', 'text', ... 'String', '频率: 0 Hz', 'Position', [20, 20, 200, 30], ... 'FontSize', 12, 'FontWeight', 'bold'); % 故障检测面板 handles.faultPanel = uipanel('Parent', controlPanel, 'Title', '故障检测', ... 'Position', [0.05, 0.35, 0.9, 0.18], 'Units', 'normalized', ... 'BackgroundColor', [0.95, 0.95, 0.95]); faultText = {'低频放大器: 正常', '中频放大器: 正常', '天线: 正常'}; handles.faultHandles = gobjects(1,3); for i = 1:3 handles.faultHandles(i) = uicontrol('Parent', handles.faultPanel, 'Style', 'text', ... 'String', faultText{i}, 'Position', [20, 100-30*i, 250, 25], ... 'FontSize', 11, 'FontWeight', 'bold'); end % 标准值和容差设置面板 handles.settingsPanel = uipanel('Parent', controlPanel, 'Title', '通道参数设置', ... 'Position', [0.05, 0.05, 0.9, 0.28], 'Units', 'normalized', ... 'BackgroundColor', [0.95, 0.95, 0.95]); labels = {'通道1标准值:', '通道1容差:', '通道2标准值:', '通道2容差:', ... '通道3标准值:', '通道3容差:', '通道4标准值:', '通道4容差:'}; handles.stdHandles = gobjects(1,4); handles.tolHandles = gobjects(1,4); for i = 1:4 % 标准值设置 uicontrol('Parent', handles.settingsPanel, 'Style', 'text', ... 'String', labels{2*i-1}, 'Position', [20, 160-30*i, 100, 20], ... 'FontSize', 9); handles.stdHandles(i) = uicontrol('Parent', handles.settingsPanel, 'Style', 'edit', ... 'String', '1.5', 'Position', [130, 160-30*i, 60, 25], ... 'Tag', sprintf('std%d', i), 'Callback', @updateSettings, ... 'FontSize', 9, 'BackgroundColor', 'white'); % 容差设置 uicontrol('Parent', handles.settingsPanel, 'Style', 'text', ... 'String', labels{2*i}, 'Position', [200, 160-30*i, 80, 20], ... 'FontSize', 9); handles.tolHandles(i) = uicontrol('Parent', handles.settingsPanel, 'Style', 'edit', ... 'String', '1.0', 'Position', [280, 160-30*i, 60, 25], ... 'Tag', sprintf('tol%d', i), 'Callback', @updateSettings, ... 'FontSize', 9, 'BackgroundColor', 'white'); end % 状态提示 (底部) handles.statusPanel = uipanel('Parent', fig, 'Title', '系统状态', ... 'Position', [0.02, 0.01, 0.96, 0.03], 'Units', 'normalized', ... 'BackgroundColor', [0.8, 0.8, 0.8]); handles.statusText = uicontrol('Parent', handles.statusPanel, 'Style', 'text', ... 'String', '等待选择文件...', 'Position', [10, 5, 1200, 20], ... 'FontSize', 9, 'HorizontalAlignment', 'left', 'BackgroundColor', [0.8, 0.8, 0.8]); % 存储所有句柄 setappdata(fig, 'handles', handles); % 添加关闭回调函数 function closeFigureCallback(~, ~) % 确保停止定时器 data = getappdata(fig, 'data'); if ~isempty(data.timer) && isvalid(data.timer) && strcmp(data.timer.Running, 'on') stop(data.timer); delete(data.timer); end % 安全关闭文件 if data.fileID > 2 try fclose(data.fileID); catch end end % 删除图形 delete(fig); end % 文件选择函数 function selectFile(~, ~) data = getappdata(fig, 'data'); handles = getappdata(fig, 'handles'); % 文件选择 [filename, pathname] = uigetfile('*.dat', '选择DAT数据文件'); if isequal(filename, 0) || isequal(pathname, 0) return; end % 显示文件路径 fullpath = fullfile(pathname, filename); set(handles.filePathDisplay, 'String', ['文件: ' fullpath], 'Tooltip', fullpath); set(handles.statusText, 'String', '文件已选择,准备开始检测...'); % 关闭之前打开的文件 if data.fileID > 2 try fclose(data.fileID); catch end end % 打开新文件 - 使用'rt'模式确保跨平台兼容 fid = fopen(fullpath, 'rt'); if fid == -1 errordlg('无法打开文件!', '文件错误'); set(handles.statusText, 'String', '错误:无法打开文件'); return; end % 重置数据 data.fileID = fid; data.startPosition = 0; data.lastUpdate = now; data.startTime = now; % 重置开始时间 for i = 1:4 data.voltageData{i} = []; data.timeData{i} = []; data.filterData{i} = []; end % 更新图形 for i = 1:4 cla(axesHandles(i)); handles.plotHandles(i) = plot(axesHandles(i), 0, 0, 'Color', colors{i}, 'LineWidth', 1.5); title(axesHandles(i), titles{i}, 'FontSize', 10); grid(axesHandles(i), 'on'); hold(axesHandles(i), 'on'); end % 存储更新后的数据 setappdata(fig, 'data', data); % 启用开始按钮 set(handles.startBtn, 'Enable', 'on'); end % 开始/停止监测 function toggleMonitoring(~, ~) data = getappdata(fig, 'data'); handles = getappdata(fig, 'handles'); if data.running % 停止监测 data.running = false; if ~isempty(data.timer) && isvalid(data.timer) && strcmp(data.timer.Running, 'on') stop(data.timer); end set(handles.startBtn, 'String', '开始检测', 'BackgroundColor', [0.4, 0.8, 0.4]); set(handles.statusText, 'String', '检测已停止'); else % 开始监测 if data.fileID <= 2 errordlg('请先选择DAT文件!', '错误'); set(handles.statusText, 'String', '错误:请先选择DAT文件'); return; end data.running = true; data.startTime = now; % 记录监测开始时间 set(handles.startBtn, 'String', '停止检测', 'BackgroundColor', [0.9, 0.4, 0.4]); set(handles.statusText, 'String', '检测进行中...'); % 记录起始位置 fseek(data.fileID, 0, 'eof'); data.startPosition = ftell(data.fileID); data.lastUpdate = now; % 创建定时器(兼容R2016a) data.timer = timer(... 'ExecutionMode', 'fixedRate', ... 'Period', 0.5, ... 'TimerFcn', @(~,~)updatePlots(), ... 'ErrorFcn', @timerErrorCallback); start(data.timer); end % 存储更新后的数据 setappdata(fig, 'data', data); end % 定时器错误处理 function timerErrorCallback(~, event) errordlg(sprintf('定时器错误: %s', event.Data.message), '定时器错误'); setappdata(fig, 'handles').statusText.String = '定时器错误'; stopMonitoring(); end % 结束检测 function stopMonitoring(~, ~) data = getappdata(fig, 'data'); handles = getappdata(fig, 'handles'); data.running = false; set(handles.startBtn, 'String', '开始检测', 'BackgroundColor', [0.4, 0.8, 0.4]); set(handles.statusText, 'String', '检测已停止'); if ~isempty(data.timer) && isvalid(data.timer) && strcmp(data.timer.Running, 'on') stop(data.timer); end setappdata(fig, 'data', data); end % 更新设置 function updateSettings(src, ~) data = getappdata(fig, 'data'); tag = get(src, 'Tag'); idx = str2double(tag(end)); value = str2double(get(src, 'String')); if isnan(value) errordlg('请输入有效数字!', '输入错误'); if strncmp(tag, 'std', 3) set(src, 'String', num2str(data.standards(idx))); else set(src, 'String', num2str(data.tolerances(idx))); end return; end if strncmp(tag, 'std', 3) data.standards(idx) = value; elseif strncmp(tag, 'tol', 3) data.tolerances(idx) = value; end setappdata(fig, 'data', data); setappdata(fig, 'handles').statusText.String = '参数已更新'; end % 更新图形和数据 - 修复版 function updatePlots() figHandle = fig; % 使用本地变量 if ~ishandle(figHandle) return; end data = getappdata(figHandle, 'data'); handles = getappdata(figHandle, 'handles'); axesHandles = [handles.ax1, handles.ax2, handles.ax3, handles.ax4]; if ~data.running || data.fileID <= 2 return; end try % 获取当前文件大小 fseek(data.fileID, 0, 'eof'); endPosition = ftell(data.fileID); % 如果有新数据 if endPosition > data.startPosition fseek(data.fileID, data.startPosition, 'bof'); dataLines = {}; % 存储所有有效行 validLines = 0; % 读取新行 - 处理空行和组数据 while ~feof(data.fileID) tline = fgetl(data.fileID); if ischar(tline) % 跳过空行 tline = strtrim(tline); if isempty(tline) continue; end % 存储有效行 dataLines{end+1} = tline; validLines = validLines + 1; end end % 处理完整组(4行一组) numGroups = floor(validLines/4); if numGroups > 0 % 提取各组数据 groupData = zeros(numGroups, 4); % 预分配矩阵 for g = 1:numGroups % 获取当前组的4行数据 startIdx = (g-1)*4 + 1; for i = 1:4 lineIdx = startIdx + i - 1; if lineIdx > length(dataLines) break; end % 解析电压值 tline = dataLines{lineIdx}; % 使用正则表达式提取电压值 tokens = regexp(tline, 'CH\w:\s*([\d\.]+)V?', 'tokens'); if ~isempty(tokens) numValue = str2double(tokens{1}{1}); if ~isnan(numValue) groupData(g, i) = numValue; else set(handles.statusText, 'String', '警告:电压值转换失败'); groupData(g, i) = 0; % 使用默认值 end else % 备选解析方法:直接提取数字部分 numStr = regexp(tline, '[\d\.]+', 'match', 'once'); if ~isempty(numStr) numValue = str2double(numStr); if ~isnan(numValue) groupData(g, i) = numValue; else set(handles.statusText, 'String', '警告:正则提取值转换失败'); groupData(g, i) = 0; % 使用默认值 end else set(handles.statusText, 'String', '警告:未找到电压值'); groupData(g, i) = 0; % 使用默认值 end end end end % 更新时间序列(使用相对时间) currentTime = now; timeStep = 0.5; % 采样间隔为0.5秒 for i = 1:4 % 更新电压数据 newVoltages = groupData(:, i); data.voltageData{i} = [data.voltageData{i}; newVoltages]; % 更新时间数据(使用相对时间) if isempty(data.timeData{i}) % 第一次更新,创建时间序列 startTime = 0; newTimes = (0:numGroups-1)' * timeStep; else % 后续更新,继续时间序列 lastTime = data.timeData{i}(end); startTime = lastTime + timeStep; newTimes = (startTime:timeStep:startTime+(numGroups-1)*timeStep)'; end data.timeData{i} = [data.timeData{i}; newTimes]; % 应用滤波(移动平均) if length(data.voltageData{i}) > 5 % 5点移动平均滤波 b = ones(5,1)/5; data.filterData{i} = filter(b, 1, data.voltageData{i}); else data.filterData{i} = data.voltageData{i}; end % 更新图形(只显示滤波后数据) set(handles.plotHandles(i), 'XData', data.timeData{i}, 'YData', data.filterData{i}); title(axesHandles(i), titles{i}, 'FontSize', 10); grid(axesHandles(i), 'on'); xlabel(axesHandles(i), '时间 (s)', 'FontSize', 8); ylabel(axesHandles(i), '电压 (V)', 'FontSize', 8); % 设置合适的Y轴范围 if ~isempty(data.filterData{i}) minVal = min(data.filterData{i}); maxVal = max(data.filterData{i}); range = max(0.1, maxVal - minVal); % 确保范围不为零 ylim(axesHandles(i), [minVal - 0.1*range, maxVal + 0.1*range]); end % 设置合适的X轴范围 if ~isempty(data.timeData{i}) maxTime = max(data.timeData{i}); minTime = max(0, maxTime - 30); % 显示最近30秒数据 xlim(axesHandles(i), [minTime, maxTime + 1]); end end % 计算通道4的频率(滤波前的电压值) if ~isempty(data.voltageData{4}) % 电压转换为频率(假设转换公式:f = 50 * voltage) lastVoltage = data.voltageData{4}(end); frequency = 50 * lastVoltage; set(handles.freqDisplay, 'String', sprintf('频率: %.2f Hz', frequency)); end % 故障检测 checkFaults(); % 更新图形 drawnow; % 更新状态 set(handles.statusText, 'String', sprintf('已处理 %d 组新数据 (%d)', numGroups, validLines)); else set(handles.statusText, 'String', '没有完整组数据'); end % 更新起始位置 data.startPosition = endPosition; data.lastUpdate = currentTime; else % 如果没有新数据,检查文件是否被重置 if endPosition < data.startPosition % 文件可能被重置(如SSCOM重新开始记录) data.startPosition = 0; set(handles.statusText, 'String', '文件被重置,从开头重新读取...'); end end catch ME errordlg(sprintf('更新图形时出错: %s', ME.message), '运行时错误'); set(handles.statusText, 'String', ['错误: ' ME.message]); stopMonitoring(); end % 存储更新后的数据 setappdata(figHandle, 'data', data); end % 故障检测 function checkFaults() data = getappdata(fig, 'data'); handles = getappdata(fig, 'handles'); % 检查是否有足够的数据 if isempty(data.filterData) || length(data.filterData) < 4 || ... isempty(data.filterData{1}) || isempty(data.filterData{2}) || isempty(data.filterData{4}) return; end % 获取最新滤波后的电压值 latestVals = [data.filterData{1}(end), data.filterData{2}(end), data.filterData{4}(end)]; % 故障状态文本 faultStatus = {'正常', '异常'}; faultColors = {[0,0.5,0], [1,0,0]}; % 正常绿色,异常红色 % 检查每个相关通道 for i = 1:3 chIdx = i; % 通道索引:1->低频放大器, 2->中频放大器, 4->天线 if i == 3 chIdx = 4; % 天线对应通道4 end lowerBound = data.standards(chIdx) - data.tolerances(chIdx); upperBound = data.standards(chIdx) + data.tolerances(chIdx); currentVal = latestVals(i); % 检查是否超出范围 if currentVal < lowerBound || currentVal > upperBound status = 2; % 异常 else status = 1; % 正常 end % 更新故障显示 switch i case 1 msg = sprintf('低频放大器: %s', faultStatus{status}); case 2 msg = sprintf('中频放大器: %s', faultStatus{status}); case 3 msg = sprintf('天线: %s', faultStatus{status}); end set(handles.faultHandles(i), 'String', msg, 'ForegroundColor', faultColors{status}); end end end
07-01
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值