关于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 %% 加载MAT文件 fprintf('正在加载文件 A.mat...\n'); load('A.mat'); %% 获取工作区中的所有变量 vars = whos('-file', 'A.mat'); fprintf('文件中的变量:\n'); fprintf('=' * 50); fprintf('\n'); %% 显示变量信息 for i = 1:length(vars) fprintf('变量名: %s\n', vars(i).name); fprintf('类型: %s\n', vars(i).class); fprintf('大小: %s\n', mat2str(vars(i).size)); fprintf('字节大小: %.2f KB\n', vars(i).bytes/1024); fprintf('-' * 30); fprintf('\n'); end %% 找到主要数据变量 mainDataVars = {}; for i = 1:length(vars) if ~any(strcmp(vars(i).name, {'__header__', '__version__', '__globals__'})) mainDataVars{end+1} = vars(i).name; end end fprintf('\n主要数据变量: %s\n', strjoin(mainDataVars, ', ')); fprintf('=' * 50); fprintf('\n'); %% 对每个主要变量进行分析 for i = 1:length(mainDataVars) varName = mainDataVars{i}; data = eval(varName); fprintf('\n分析变量: %s\n', varName); analyzeVariable(data, varName); end %% 检查标签数据 checkLabels(vars); % createVisualizations.m function createVisualizations(data, varName) % 创建各种可视化图表 if ~isnumeric(data) fprintf('数据不是数值类型,跳过可视化\n'); return; end % 重塑数据为2D矩阵(如果必要) if ndims(data) > 2 fprintf('数据维度超过2D,无法进行标准可视化\n'); return; end [rows, cols] = size(data); % 1. 特征分布直方图 if cols > 1 figure('Name', sprintf('%s - 特征分布', varName), 'Position', [100, 100, 1200, 800]); nPlots = min(9, cols); for i = 1:nPlots subplot(3, 3, i); histogram(data(:, i), 50, 'FaceColor', 'blue', 'EdgeColor', 'black', 'FaceAlpha', 0.7); title(sprintf('特征 %d 分布', i)); xlabel('值'); ylabel('频次'); grid on; end sgtitle(sprintf('变量 "%s" 的特征分布直方图', varName), 'FontSize', 16); end % 2. 箱线图 if cols > 1 figure('Name', sprintf('%s - 箱线图', varName), 'Position', [100, 100, 1000, 600]); boxplot(data(:, 1:min(20, cols))); title(sprintf('变量 "%s" 的特征箱线图', varName)); xlabel('特征索引'); ylabel('值'); grid on; rotateXLabels(gca, 45); end % 3. 相关性热图 if cols <= 50 && cols > 1 figure('Name', sprintf('%s - 相关性热图', varName), 'Position', [100, 100, 800, 700]); corrMatrix = corr(data, 'Rows', 'pairwise'); imagesc(corrMatrix); colorbar; colormap(jet); title(sprintf('变量 "%s" 的特征相关性热图', varName)); xlabel('特征索引'); ylabel('特征索引'); set(gca, 'XTick', 1:cols, 'YTick', 1:cols); end % 4. 趋势图(如果是时间序列数据) if rows > 100 && cols >= 1 % 只有数据足够长时才显示趋势图 figure('Name', sprintf('%s - 趋势图', varName), 'Position', [100, 100, 1200, 800]); nSeries = min(5, cols); for i = 1:nSeries subplot(2, 3, i); plot(data(:, i), 'b-', 'LineWidth', 1.5); title(sprintf('特征 %d 趋势图', i)); xlabel('样本索引'); ylabel('值'); grid on; end sgtitle(sprintf('变量 "%s" 的特征趋势图', varName), 'FontSize', 16); end % 5. 数据质量检查图 figure('Name', sprintf('%s - 数据质量', varName), 'Position', [100, 100, 800, 600]); % 缺失值检查 missingCount = sum(isnan(data), 1); subplot(2, 2, 1); bar(missingCount); title('各特征缺失值数量'); xlabel('特征索引'); ylabel('缺失值数量'); grid on; % 异常值检查(使用3σ原则) if cols > 1 outlierCount = zeros(1, min(20, cols)); for i = 1:min(20, cols) colData = data(:, i); validData = colData(~isnan(colData)); if ~isempty(validData) meanVal = mean(validData); stdVal = std(validData); outlierCount(i) = sum(abs(validData - meanVal) > 3 * stdVal); end end subplot(2, 2, 2); bar(outlierCount); title('各特征异常值数量 (3σ)'); xlabel('特征索引'); ylabel('异常值数量'); grid on; end end % analyzeVariable.m function analyzeVariable(data, varName) % 分析单个变量的函数 fprintf('数据类型: %s\n', class(data)); fprintf('数据维度: %s\n', mat2str(size(data))); % 显示数据示例 if ndims(data) == 2 [rows, cols] = size(data); fprintf('数据形状: %d × %d\n', rows, cols); if rows > 0 && cols > 0 fprintf('前5行5列数据:\n'); disp(data(1:min(5,rows), 1:min(5,cols))); end end % 数据统计信息 fprintf('\n统计信息:\n'); if isnumeric(data) && ndims(data) == 2 statsSummary = []; nFeatures = min(10, size(data, 2)); for j = 1:nFeatures colData = data(:, j); validData = colData(~isnan(colData) & ~isinf(colData)); if ~isempty(validData) statsSummary(j).Feature = j; statsSummary(j).Mean = mean(validData); statsSummary(j).Std = std(validData); statsSummary(j).Min = min(validData); statsSummary(j).Max = max(validData); statsSummary(j).Missing = sum(isnan(colData)); end end % 显示统计表格 if ~isempty(statsSummary) fprintf('%-10s %-10s %-10s %-10s %-10s %-10s\n', ... '特征', '均值', '标准差', '最小值', '最大值', '缺失值'); fprintf('-' * 60); fprintf('\n'); for j = 1:length(statsSummary) fprintf('%-10d %-10.4f %-10.4f %-10.4f %-10.4f %-10d\n', ... statsSummary(j).Feature, statsSummary(j).Mean, ... statsSummary(j).Std, statsSummary(j).Min, ... statsSummary(j).Max, statsSummary(j).Missing); end end end % 创建可视化 createVisualizations(data, varName); fprintf('-' * 50); fprintf('\n'); end
09-22
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
function untitled() % 主函数 - 挑流水舌时间序列整体分析(支持.dat和.cas的H5文件) main_water_jet_analysis_unified(); end function main_water_jet_analysis_unified() % 主分析函数:同时支持.dat和.cas的HDF5时间序列文件 try % 1. 扩展筛选:同时查找包含".dat"和".cas"的HDF5文件 all_h5_files = dir('*.h5'); if isempty(all_h5_files) fprintf('当前目录下没有找到任何H5文件!\n'); return; end time_series_files = []; for i = 1:length(all_h5_files) if contains(all_h5_files(i).name, '.dat') || contains(all_h5_files(i).name, '.cas') time_series_files = [time_series_files; all_h5_files(i)]; %#ok<AGROW> end end % 2. 检查筛选结果 if isempty(time_series_files) fprintf('未找到文件名包含 ".dat" 或 ".cas" 的HDF5文件!\n'); fprintf('当前目录下所有H5文件:\n'); for i = 1:length(all_h5_files) fprintf(' - %s\n', all_h5_files(i).name); end fprintf('是否生成模拟时间序列数据?(y/n):'); response = input('', 's'); if strcmpi(response, 'y') generate_simulated_time_series(); % 重新筛选 all_h5_files = dir('*.h5'); time_series_files = []; for i = 1:length(all_h5_files) if contains(all_h5_files(i).name, '.dat') || contains(all_h5_files(i).name, '.cas') time_series_files = [time_series_files; all_h5_files(i)]; %#ok<AGROW> end end else fprintf('分析终止。\n'); return; end end % 3. 文件分类和排序 dat_files = []; cas_files = []; for i = 1:length(time_series_files) if contains(time_series_files(i).name, '.dat') dat_files = [dat_files; time_series_files(i)]; %#ok<AGROW> elseif contains(time_series_files(i).name, '.cas') cas_files = [cas_files; time_series_files(i)]; %#ok<AGROW> end end % 分别对.dat和.cas文件排序 if ~isempty(dat_files) [~, sort_idx] = sort({dat_files.name}); dat_files = dat_files(sort_idx); end if ~isempty(cas_files) [~, sort_idx] = sort({cas_files.name}); cas_files = cas_files(sort_idx); end % 4. 显示文件信息 fprintf('\n========================================\n'); fprintf(' 挑流水舌时间序列分析(支持.dat和.cas)\n'); fprintf('========================================\n'); if ~isempty(dat_files) fprintf('检测到 %d 个.dat时间序列文件:\n', length(dat_files)); for i = 1:length(dat_files) fprintf(' DAT %d: %s\n', i, dat_files(i).name); end end if ~isempty(cas_files) fprintf('检测到 %d 个.cas时间序列文件:\n', length(cas_files)); for i = 1:length(cas_files) fprintf(' CAS %d: %s\n', i, cas_files(i).name); end end % 5. 询问用户如何处理不同类型的文件 fprintf('\n请选择分析模式:\n'); fprintf(' 1. 分别分析.dat和.cas文件(生成两个独立报告)\n'); fprintf(' 2. 合并分析所有文件(生成一个综合报告)\n'); fprintf(' 3. 只分析.dat文件\n'); fprintf(' 4. 只分析.cas文件\n'); fprintf('请输入选择 (1-4): '); analysis_mode = input('', 's'); % 根据选择确定要分析的文件 files_to_analyze = []; analysis_type = ''; switch analysis_mode case '1' % 分别分析 if ~isempty(dat_files) fprintf('\n开始分析.dat文件...\n'); analyze_file_set(dat_files, 'DAT'); end if ~isempty(cas_files) fprintf('\n开始分析.cas文件...\n'); analyze_file_set(cas_files, 'CAS'); end return; case '2' % 合并分析 files_to_analyze = [dat_files; cas_files]; [~, sort_idx] = sort({files_to_analyze.name}); files_to_analyze = files_to_analyze(sort_idx); analysis_type = 'COMBINED'; case '3' % 只分析.dat if isempty(dat_files) fprintf('没有找到.dat文件!\n'); return; end files_to_analyze = dat_files; analysis_type = 'DAT'; case '4' % 只分析.cas if isempty(cas_files) fprintf('没有找到.cas文件!\n'); return; end files_to_analyze = cas_files; analysis_type = 'CAS'; otherwise fprintf('无效选择,使用默认模式(合并分析)\n'); files_to_analyze = [dat_files; cas_files]; [~, sort_idx] = sort({files_to_analyze.name}); files_to_analyze = files_to_analyze(sort_idx); analysis_type = 'COMBINED'; end % 6. 执行分析 if ~isempty(files_to_analyze) fprintf('\n开始分析%s文件...\n', analysis_type); analyze_file_set(files_to_analyze, analysis_type); else fprintf('没有找到要分析的文件!\n'); end catch ME fprintf('\n错误发生:%s\n', ME.message); if ~isempty(ME.stack) fprintf('在函数:%s\n', ME.stack(1).name); fprintf('行号:%d\n', ME.stack(1).line); end fprintf('\n建议:检查H5文件路径或数据结构是否正确\n'); end end function analyze_file_set(file_set, analysis_type) % 分析一组文件 n_time_steps = length(file_set); % 显示文件信息 fprintf('\n分析文件列表(%s类型):\n', analysis_type); for i = 1:n_time_steps fprintf(' 时间步 %d: %s\n', i, file_set(i).name); end % 自动探测数据路径(使用第一个文件作为模板) fprintf('\n正在自动探测数据路径结构...\n'); first_file = file_set(1).name; [detected_paths, all_available_paths] = auto_detect_all_paths(first_file); if isempty(detected_paths.coords) fprintf('自动探测失败,启动交互式路径选择...\n'); detected_paths = interactive_path_selection(first_file, all_available_paths); else fprintf('自动探测成功!\n'); fprintf(' 坐标路径: %s\n', detected_paths.coords); fprintf(' 速度路径: %s\n', detected_paths.velocity); fprintf(' 压力路径: %s\n', detected_paths.pressure); fprintf(' 体积分数路径: %s\n', detected_paths.volume_frac); % 验证路径有效性 fprintf('\n验证路径有效性...\n'); if validate_paths(first_file, detected_paths) fprintf('路径验证成功!\n'); else fprintf('路径验证失败,启动交互式路径选择...\n'); detected_paths = interactive_path_selection(first_file, all_available_paths); end end % 确认使用探测到的路径(简化确认流程) if ~isempty(detected_paths.coords) fprintf('\n使用探测到的路径分析所有时间步文件...\n'); else fprintf('无法确定数据路径,分析终止。\n'); return; end % 预分配存储空间 - 先获取总节点数 fprintf('\n正在计算总数据量...\n'); total_nodes = 0; node_counts = zeros(n_time_steps, 1); for time_step = 1:n_time_steps file_name = file_set(time_step).name; data = read_h5_data_with_paths(file_name, detected_paths); node_counts(time_step) = size(data.coordinates, 1); total_nodes = total_nodes + node_counts(time_step); end fprintf('总节点数: %d\n', total_nodes); % 预分配数组 all_coordinates = zeros(total_nodes, 3); all_velocity = zeros(total_nodes, 3); all_pressure = zeros(total_nodes, 1); all_volume_fraction = zeros(total_nodes, 1); time_step_markers = zeros(n_time_steps, 2); file_info = cell(n_time_steps, 5); % 文件名、类型、节点数、时间步、时间标签 % 整合所有时间序列数据 current_index = 1; for time_step = 1:n_time_steps file_name = file_set(time_step).name; file_type = get_file_type(file_name); fprintf('正在读取时间步 %d/%d: %s (%s)\n', time_step, n_time_steps, file_name, file_type); % 读取当前时间步数据 data = read_h5_data_with_paths(file_name, detected_paths); % 记录当前时间步的节点范围 n_current = node_counts(time_step); idx_range = current_index:(current_index + n_current - 1); time_step_markers(time_step, :) = [current_index, current_index + n_current - 1]; % 存储数据到预分配数组 all_coordinates(idx_range, :) = data.coordinates; all_velocity(idx_range, :) = data.velocity; all_pressure(idx_range) = data.pressure; all_volume_fraction(idx_range) = data.volume_fraction; current_index = current_index + n_current; % 存储文件信息 time_label = extract_time_label(file_name, time_step); file_info{time_step, 1} = file_name; file_info{time_step, 2} = file_type; file_info{time_step, 3} = n_current; file_info{time_step, 4} = time_step; file_info{time_step, 5} = time_label; end % 创建整合数据对象 integrated_data = struct(); integrated_data.coordinates = all_coordinates; integrated_data.velocity = all_velocity; integrated_data.pressure = all_pressure; integrated_data.volume_fraction = all_volume_fraction; integrated_data.time_step_markers = time_step_markers; integrated_data.file_info = file_info; integrated_data.n_time_steps = n_time_steps; integrated_data.total_nodes = total_nodes; integrated_data.time_labels = {file_info{:,5}}; integrated_data.file_types = {file_info{:,2}}; integrated_data.analysis_type = analysis_type; integrated_data.data_paths = detected_paths; % 执行整合分析 fprintf('\n开始时间序列数据分析...\n'); integrated_results = analyze_integrated_jet_data(integrated_data); % 生成整体报告 fprintf('生成分析报告...\n'); generate_unified_time_series_report(integrated_data, integrated_results); % 生成可视化分析 fprintf('生成可视化图表...\n'); generate_time_series_visualization(integrated_data, integrated_results); fprintf('\n========================================\n'); fprintf('%s类型时间序列分析完成!\n', analysis_type); fprintf('生成文件清单:\n'); fprintf(' 1. 分析报告:water_jet_%s_analysis_report.txt\n', lower(analysis_type)); fprintf(' 2. 可视化图表:time_series_%s_analysis.fig\n', lower(analysis_type)); fprintf(' 3. 数据汇总:time_series_%s_data.mat\n', lower(analysis_type)); fprintf('========================================\n'); end function file_type = get_file_type(file_name) % 获取文件类型 if contains(file_name, '.dat') file_type = 'DAT'; elseif contains(file_name, '.cas') file_type = 'CAS'; else file_type = 'UNKNOWN'; end end % ==================== 路径自动探测函数 ==================== function [detected_paths, all_paths] = auto_detect_all_paths(file_name) % 自动探测所有必要的数据路径 detected_paths = struct('coords', '', 'velocity', '', 'pressure', '', 'volume_frac', ''); % 获取所有可用路径 try info = h5info(file_name); all_paths = get_all_h5_datasets(info, ''); catch ME fprintf('读取H5文件信息失败: %s\n', ME.message); all_paths = {}; return; end if isempty(all_paths) fprintf('未找到任何数据路径\n'); return; end % 显示找到的所有路径(调试用) fprintf('找到 %d 个数据路径:\n', length(all_paths)); for i = 1:min(10, length(all_paths)) % 只显示前10个 fprintf(' %s\n', all_paths{i}); end if length(all_paths) > 10 fprintf(' ... 还有 %d 个路径\n', length(all_paths) - 10); end % 定义各种可能的关键词组合(按优先级排序) coord_keywords = { {'coordinates', 'coord', 'node', 'position', 'xyz', 'mesh', 'point'}, % 高优先级 {'x', 'y', 'z', 'location'}, % 中优先级 {'geometry', 'grid', 'topology'} % 低优先级 }; velocity_keywords = { {'velocity', 'vel', 'u', 'v', 'w', 'speed'}, % 高优先级 {'flow', 'vector', 'momentum'}, % 中优先级 {'field', 'data'} % 低优先级 }; pressure_keywords = { {'pressure', 'press', 'p'}, % 高优先级 {'force', 'stress', 'load'}, % 中优先级 {'scalar', 'field'} % 低优先级 }; volume_frac_keywords = { {'volume', 'frac', 'fraction', 'phase', 'vof', 'alpha'}, % 高优先级 {'concentration', 'ratio', 'percentage'}, % 中优先级 {'field', 'scalar'} % 低优先级 }; % 探测坐标路径 detected_paths.coords = find_best_path(all_paths, coord_keywords, '坐标'); % 探测速度路径 detected_paths.velocity = find_best_path(all_paths, velocity_keywords, '速度'); % 探测压力路径 detected_paths.pressure = find_best_path(all_paths, pressure_keywords, '压力'); % 探测体积分数路径 detected_paths.volume_frac = find_best_path(all_paths, volume_frac_keywords, '体积分数'); end function best_path = find_best_path(all_paths, keyword_groups, data_type) % 根据关键词组找到最佳路径 best_path = ''; best_priority = inf; % 按优先级顺序搜索 for priority = 1:length(keyword_groups) keywords = keyword_groups{priority}; for i = 1:length(all_paths) path_lower = lower(all_paths{i}); for j = 1:length(keywords) if contains(path_lower, lower(keywords{j})) % 找到匹配,记录最佳优先级 if priority < best_priority best_path = all_paths{i}; best_priority = priority; fprintf(' 找到%s路径: %s (优先级%d)\n', data_type, best_path, priority); end end end end % 如果找到高优先级的路径,直接返回 if ~isempty(best_path) && best_priority <= 2 return; end end if isempty(best_path) fprintf(' 未找到%s路径\n', data_type); end end function is_valid = validate_paths(file_name, paths) % 验证路径组合是否有效 is_valid = false; try % 测试读取坐标数据 coords = h5read(file_name, paths.coords); % 确保坐标数据是N×3格式 if size(coords, 2) ~= 3 && size(coords, 1) == 3 coords = coords'; end if size(coords, 2) ~= 3 fprintf(' 坐标数据维度不正确: %s\n', mat2str(size(coords))); return; end % 测试读取速度数据 velocity = h5read(file_name, paths.velocity); % 确保速度数据是N×3格式 if size(velocity, 2) ~= 3 && size(velocity, 1) == 3 velocity = velocity'; end if size(velocity, 2) ~= 3 fprintf(' 速度数据维度不正确: %s\n', mat2str(size(velocity))); return; end % 测试读取压力数据 pressure = h5read(file_name, paths.pressure); pressure = pressure(:); % 确保是列向量 % 测试读取体积分数数据 volume_frac = h5read(file_name, paths.volume_frac); volume_frac = volume_frac(:); % 确保是列向量 % 检查节点数一致性 n_nodes = size(coords, 1); if size(velocity, 1) ~= n_nodes fprintf(' 速度数据节点数不一致: 坐标=%d, 速度=%d\n', n_nodes, size(velocity,1)); return; end if length(pressure) ~= n_nodes fprintf(' 压力数据节点数不一致: 坐标=%d, 压力=%d\n', n_nodes, length(pressure)); return; end if length(volume_frac) ~= n_nodes fprintf(' 体积分数数据节点数不一致: 坐标=%d, 体积分数=%d\n', n_nodes, length(volume_frac)); return; end is_valid = true; fprintf(' 路径验证通过: 所有数据维度正确,节点数=%d\n', n_nodes); catch ME fprintf(' 路径验证失败: %s\n', ME.message); end end % ==================== 交互式路径选择 ==================== function selected_paths = interactive_path_selection(file_name, all_paths) % 交互式路径选择界面 fprintf('\n========================================\n'); fprintf('交互式路径选择 - %s\n', file_name); fprintf('========================================\n'); selected_paths = struct('coords', '', 'velocity', '', 'pressure', '', 'volume_frac', ''); % 显示所有可用路径 if isempty(all_paths) fprintf('没有可用的数据路径!\n'); return; end fprintf('\n可用数据路径列表:\n'); for i = 1:length(all_paths) fprintf(' [%2d] %s\n', i, all_paths{i}); end % 选择坐标路径 fprintf('\n请选择坐标数据路径(包含X,Y,Z坐标):\n'); selected_paths.coords = select_path_interactive(all_paths, '坐标'); % 选择速度路径 fprintf('\n请选择速度数据路径(包含Vx,Vy,Vz分量):\n'); selected_paths.velocity = select_path_interactive(all_paths, '速度'); % 选择压力路径 fprintf('\n请选择压力数据路径:\n'); selected_paths.pressure = select_path_interactive(all_paths, '压力'); % 选择体积分数路径 fprintf('\n请选择体积分数数据路径:\n'); selected_paths.volume_frac = select_path_interactive(all_paths, '体积分数'); % 验证选择的路径 fprintf('\n验证选择的路径...\n'); if validate_paths(file_name, selected_paths) fprintf('路径选择完成!\n'); else fprintf('路径验证失败,请重新选择\n'); selected_paths = interactive_path_selection(file_name, all_paths); end end function selected_path = select_path_interactive(all_paths, data_type) % 交互式选择单个路径 while true fprintf('输入路径编号 (1-%d) 或输入完整路径: ', length(all_paths)); user_input = input('', 's'); % 检查是否为数字选择 choice_num = str2double(user_input); if ~isnan(choice_num) && choice_num >= 1 && choice_num <= length(all_paths) selected_path = all_paths{choice_num}; fprintf('已选择: %s\n', selected_path); break; else % 检查是否为有效路径 if ~isempty(user_input) selected_path = user_input; fprintf('已输入: %s\n', selected_path); break; else fprintf('输入无效,请重新选择\n'); end end end end % ==================== 数据读取函数 ==================== function data = read_h5_data_with_paths(file_name, paths) % 使用指定路径读取HDF5文件数据 data = struct(); data.file_name = file_name; try % 读取坐标数据 coords_data = h5read(file_name, paths.coords); % 确保坐标数据是N×3格式 if size(coords_data, 2) ~= 3 && size(coords_data, 1) == 3 coords_data = coords_data'; end if size(coords_data, 2) ~= 3 error('坐标数据维度不正确,期望N×3,实际为%s', mat2str(size(coords_data))); end data.coordinates = coords_data; % 读取速度数据 vel_data = h5read(file_name, paths.velocity); % 确保速度数据是N×3格式 if size(vel_data, 2) ~= 3 && size(vel_data, 1) == 3 vel_data = vel_data'; end if size(vel_data, 2) ~= 3 error('速度数据维度不正确,期望N×3,实际为%s', mat2str(size(vel_data))); end data.velocity = vel_data; % 读取压力数据 press_data = h5read(file_name, paths.pressure); data.pressure = press_data(:); % 确保是列向量 % 读取体积分数数据 vol_data = h5read(file_name, paths.volume_frac); data.volume_fraction = vol_data(:); % 确保是列向量 % 验证数据一致性 n_nodes = size(data.coordinates, 1); if size(data.velocity, 1) ~= n_nodes error('速度数据节点数(%d)与坐标节点数(%d)不匹配', size(data.velocity,1), n_nodes); end if length(data.pressure) ~= n_nodes error('压力数据节点数(%d)与坐标节点数(%d)不匹配', length(data.pressure), n_nodes); end if length(data.volume_fraction) ~= n_nodes error('体积分数数据节点数(%d)与坐标节点数(%d)不匹配', length(data.volume_fraction), n_nodes); end catch ME error('读取文件 %s 失败: %s', file_name, ME.message); end end % ==================== 时间标签提取函数 ==================== function time_label = extract_time_label(file_name, time_step) % 从文件名中提取时间标签 % 尝试从文件名中提取数字作为时间 numbers = regexp(file_name, '\d+', 'match'); if ~isempty(numbers) % 使用找到的最后一个数字作为时间 time_value = str2double(numbers{end}); time_label = sprintf('t=%.2f', time_value); else % 如果没有数字,使用时间步序号 time_label = sprintf('Step%d', time_step); end end % ==================== 分析函数 ==================== function integrated_results = analyze_integrated_jet_data(integrated_data) % 分析整合的挑流数据 integrated_results = struct(); % 基本统计信息 integrated_results.total_nodes = integrated_data.total_nodes; integrated_results.n_time_steps = integrated_data.n_time_steps; % 水相节点统计 water_mask = integrated_data.volume_fraction > 0.5; integrated_results.water_nodes = sum(water_mask); integrated_results.water_ratio = integrated_results.water_nodes / integrated_results.total_nodes * 100; % 速度统计 speed = sqrt(sum(integrated_data.velocity.^2, 2)); integrated_results.avg_speed = mean(speed); integrated_results.max_speed = max(speed); integrated_results.min_speed = min(speed); integrated_results.speed_std = std(speed); % 压力统计 integrated_results.avg_pressure = mean(integrated_data.pressure); integrated_results.max_pressure = max(integrated_data.pressure); integrated_results.min_pressure = min(integrated_data.pressure); integrated_results.pressure_std = std(integrated_data.pressure); % 水舌形态统计 if any(water_mask) water_coords = integrated_data.coordinates(water_mask, :); integrated_results.jet_length = max(water_coords(:,1)) - min(water_coords(:,1)); integrated_results.jet_height = max(water_coords(:,2)) - min(water_coords(:,2)); integrated_results.jet_width = max(water_coords(:,3)) - min(water_coords(:,3)); integrated_results.jet_centroid = mean(water_coords, 1); else integrated_results.jet_length = 0; integrated_results.jet_height = 0; integrated_results.jet_width = 0; integrated_results.jet_centroid = [0, 0, 0]; end % 时间序列演化分析 integrated_results.time_evolution = analyze_time_evolution(integrated_data); % 空间分布分析 integrated_results.spatial_analysis = analyze_spatial_distribution(integrated_data); % 相关性分析 integrated_results.correlation_analysis = analyze_correlations(integrated_data); end function time_evolution = analyze_time_evolution(integrated_data) % 分析时间序列演化特征 time_evolution = struct(); n_steps = integrated_data.n_time_steps; % 预分配数组 time_evolution.avg_speed = zeros(n_steps, 1); time_evolution.water_ratio = zeros(n_steps, 1); time_evolution.jet_length = zeros(n_steps, 1); time_evolution.jet_height = zeros(n_steps, 1); time_evolution.jet_width = zeros(n_steps, 1); time_evolution.total_nodes = zeros(n_steps, 1); for t = 1:n_steps start_idx = integrated_data.time_step_markers(t, 1); end_idx = integrated_data.time_step_markers(t, 2); % 当前时间步数据 coords_t = integrated_data.coordinates(start_idx:end_idx, :); velocity_t = integrated_data.velocity(start_idx:end_idx, :); volume_frac_t = integrated_data.volume_fraction(start_idx:end_idx); % 计算各指标 speed_t = sqrt(sum(velocity_t.^2, 2)); time_evolution.avg_speed(t) = mean(speed_t); time_evolution.water_ratio(t) = sum(volume_frac_t > 0.5) / length(volume_frac_t) * 100; time_evolution.total_nodes(t) = length(volume_frac_t); % 水舌形态 water_mask_t = volume_frac_t > 0.5; if any(water_mask_t) water_coords_t = coords_t(water_mask_t, :); time_evolution.jet_length(t) = max(water_coords_t(:,1)) - min(water_coords_t(:,1)); time_evolution.jet_height(t) = max(water_coords_t(:,2)) - min(water_coords_t(:,2)); time_evolution.jet_width(t) = max(water_coords_t(:,3)) - min(water_coords_t(:,3)); else time_evolution.jet_length(t) = 0; time_evolution.jet_height(t) = 0; time_evolution.jet_width(t) = 0; end end % 计算变化趋势 if n_steps > 1 time_evolution.speed_trend = polyfit(1:n_steps, time_evolution.avg_speed', 1); time_evolution.water_ratio_trend = polyfit(1:n_steps, time_evolution.water_ratio', 1); time_evolution.jet_length_trend = polyfit(1:n_steps, time_evolution.jet_length', 1); time_evolution.jet_height_trend = polyfit(1:n_steps, time_evolution.jet_height', 1); else time_evolution.speed_trend = [0, 0]; time_evolution.water_ratio_trend = [0, 0]; time_evolution.jet_length_trend = [0, 0]; time_evolution.jet_height_trend = [0, 0]; end end function spatial_analysis = analyze_spatial_distribution(integrated_data) % 分析空间分布特征 spatial_analysis = struct(); water_mask = integrated_data.volume_fraction > 0.5; water_coords = integrated_data.coordinates(water_mask, :); if ~isempty(water_coords) spatial_analysis.x_range = [min(water_coords(:,1)), max(water_coords(:,1))]; spatial_analysis.y_range = [min(water_coords(:,2)), max(water_coords(:,2))]; spatial_analysis.z_range = [min(water_coords(:,3)), max(water_coords(:,3))]; spatial_analysis.centroid_trajectory = calculate_centroid_trajectory(integrated_data); else spatial_analysis.x_range = [0, 0]; spatial_analysis.y_range = [0, 0]; spatial_analysis.z_range = [0, 0]; spatial_analysis.centroid_trajectory = []; end end function centroid_trajectory = calculate_centroid_trajectory(integrated_data) % 计算水舌质心随时间的变化轨迹 n_steps = integrated_data.n_time_steps; centroid_trajectory = zeros(n_steps, 3); for t = 1:n_steps start_idx = integrated_data.time_step_markers(t, 1); end_idx = integrated_data.time_step_markers(t, 2); coords_t = integrated_data.coordinates(start_idx:end_idx, :); volume_frac_t = integrated_data.volume_fraction(start_idx:end_idx); water_mask_t = volume_frac_t > 0.5; if any(water_mask_t) water_coords_t = coords_t(water_mask_t, :); centroid_trajectory(t, :) = mean(water_coords_t, 1); else centroid_trajectory(t, :) = [0, 0, 0]; end end end function correlation_analysis = analyze_correlations(integrated_data) % 分析各物理量之间的相关性 correlation_analysis = struct(); water_mask = integrated_data.volume_fraction > 0.5; if sum(water_mask) > 1 water_coords = integrated_data.coordinates(water_mask, :); water_velocity = integrated_data.velocity(water_mask, :); water_pressure = integrated_data.pressure(water_mask); speed = sqrt(sum(water_velocity.^2, 2)); % 计算相关系数 [correlation_analysis.speed_pressure_r, correlation_analysis.speed_pressure_p] = ... corr(speed, water_pressure); [correlation_analysis.height_speed_r, correlation_analysis.height_speed_p] = ... corr(water_coords(:,2), speed); [correlation_analysis.length_pressure_r, correlation_analysis.length_pressure_p] = ... corr(water_coords(:,1), water_pressure); else correlation_analysis.speed_pressure_r = 0; correlation_analysis.speed_pressure_p = 1; correlation_analysis.height_speed_r = 0; correlation_analysis.height_speed_p = 1; correlation_analysis.length_pressure_r = 0; correlation_analysis.length_pressure_p = 1; end end % ==================== 报告生成函数 ==================== function generate_unified_time_series_report(integrated_data, integrated_results) % 生成针对时间序列的整体分析报告 report_lines = {}; analysis_type = integrated_data.analysis_type; % 报告标题 report_lines{end+1} = '=================================================='; report_lines{end+1} = sprintf(' 挑流水舌时间序列分析报告 (%s)', analysis_type); report_lines{end+1} = '=================================================='; report_lines{end+1} = ''; report_lines{end+1} = sprintf('分析时间:%s', datestr(now)); report_lines{end+1} = sprintf('分析类型:%s', analysis_type); report_lines{end+1} = sprintf('时间步数量:%d', integrated_data.n_time_steps); report_lines{end+1} = sprintf('总数据节点数:%d', integrated_results.total_nodes); report_lines{end+1} = ''; % 数据路径信息 report_lines{end+1} = '一、数据路径配置'; report_lines{end+1} = '----------------------------------------'; report_lines{end+1} = sprintf('坐标数据路径:%s', integrated_data.data_paths.coords); report_lines{end+1} = sprintf('速度数据路径:%s', integrated_data.data_paths.velocity); report_lines{end+1} = sprintf('压力数据路径:%s', integrated_data.data_paths.pressure); report_lines{end+1} = sprintf('体积分数路径:%s', integrated_data.data_paths.volume_frac); report_lines{end+1} = ''; % 文件列表 report_lines{end+1} = '二、时间序列文件'; report_lines{end+1} = '----------------------------------------'; for t = 1:integrated_data.n_time_steps report_lines{end+1} = sprintf('时间步 %d: %s (%s, %s, %d节点)', t, ... integrated_data.file_info{t,1}, integrated_data.file_info{t,2}, ... integrated_data.file_info{t,5}, integrated_data.file_info{t,3}); end report_lines{end+1} = ''; % 整体统计 report_lines{end+1} = '三、整体统计信息'; report_lines{end+1} = '----------------------------------------'; report_lines{end+1} = sprintf('水相节点总数:%d (%.1f%%)', ... integrated_results.water_nodes, integrated_results.water_ratio); report_lines{end+1} = sprintf('平均水流速度:%.2f ± %.2f m/s', ... integrated_results.avg_speed, integrated_results.speed_std); report_lines{end+1} = sprintf('速度范围:[%.2f, %.2f] m/s', ... integrated_results.min_speed, integrated_results.max_speed); report_lines{end+1} = sprintf('平均压力:%.2f ± %.2f Pa', ... integrated_results.avg_pressure, integrated_results.pressure_std); report_lines{end+1} = ''; % 时间演化分析 report_lines{end+1} = '四、时间演化特征'; report_lines{end+1} = '----------------------------------------'; te = integrated_results.time_evolution; report_lines{end+1} = sprintf('速度变化趋势:y = %.4fx + %.4f', ... te.speed_trend(1), te.speed_trend(2)); report_lines{end+1} = sprintf('水相占比趋势:y = %.4fx + %.4f', ... te.water_ratio_trend(1), te.water_ratio_trend(2)); report_lines{end+1} = sprintf('水舌长度趋势:y = %.4fx + %.4f', ... te.jet_length_trend(1), te.jet_length_trend(2)); report_lines{end+1} = sprintf('水舌高度趋势:y = %.4fx + %.4f', ... te.jet_height_trend(1), te.jet_height_trend(2)); report_lines{end+1} = ''; % 详细时间步数据 report_lines{end+1} = '五、各时间步详细数据'; report_lines{end+1} = '----------------------------------------'; report_lines{end+1} = '时间步 文件类型 时间标签 平均速度(m/s) 水相占比(%) 水舌长度(m) 水舌高度(m) 节点数'; report_lines{end+1} = '-------------------------------------------------------------------------------------------------'; te = integrated_results.time_evolution; for t = 1:integrated_data.n_time_steps report_lines{end+1} = sprintf('%4d %-6s %-8s %10.2f %9.1f %10.2f %10.2f %8d', ... t, integrated_data.file_types{t}, integrated_data.time_labels{t}, ... te.avg_speed(t), te.water_ratio(t), te.jet_length(t), te.jet_height(t), te.total_nodes(t)); end report_lines{end+1} = ''; report_lines{end+1} = '=================================================='; report_lines{end+1} = ' 报告结束'; report_lines{end+1} = '=================================================='; % 保存报告 report_filename = sprintf('water_jet_%s_analysis_report.txt', lower(analysis_type)); fid = fopen(report_filename, 'w', 'n', 'UTF-8'); if fid == -1 fprintf('无法创建报告文件:%s\n', report_filename); return; end for i = 1:length(report_lines) fprintf(fid, '%s\n', report_lines{i}); end fclose(fid); fprintf('分析报告已保存至:%s\n', report_filename); end % ==================== 可视化函数 ==================== function generate_time_series_visualization(integrated_data, integrated_results) % 生成时间序列可视化分析 analysis_type = integrated_data.analysis_type; fig = figure('Position', [100, 50, 1400, 900], ... 'Name', sprintf('挑流水舌时间序列分析 (%s)', analysis_type), ... 'NumberTitle', 'off'); % 1. 时间演化趋势 subplot(2, 3, 1); te = integrated_results.time_evolution; plot(1:integrated_data.n_time_steps, te.avg_speed, 'b-o', 'LineWidth', 2); xlabel('时间步'); ylabel('平均速度 (m/s)'); title('速度随时间演化'); grid on; subplot(2, 3, 2); plot(1:integrated_data.n_time_steps, te.water_ratio, 'g-o', 'LineWidth', 2); xlabel('时间步'); ylabel('水相占比 (%)'); title('水相占比随时间演化'); grid on; subplot(2, 3, 3); plot(1:integrated_data.n_time_steps, te.jet_length, 'r-o', 'LineWidth', 2); xlabel('时间步'); ylabel('水舌长度 (m)'); title('水舌长度随时间演化'); grid on; subplot(2, 3, 4); plot(1:integrated_data.n_time_steps, te.jet_height, 'm-o', 'LineWidth', 2); xlabel('时间步'); ylabel('水舌高度 (m)'); title('水舌高度随时间演化'); grid on; % 2. 质心轨迹 subplot(2, 3, 5); centroid_traj = integrated_results.spatial_analysis.centroid_trajectory; if ~isempty(centroid_traj) && size(centroid_traj, 1) > 1 plot(centroid_traj(:,1), centroid_traj(:,2), 'k-s', 'LineWidth', 2, 'MarkerSize', 6); xlabel('X (m)'); ylabel('Y (m)'); title('水舌质心运动轨迹'); grid on; % 添加时间步标注 for t = 1:size(centroid_traj, 1) text(centroid_traj(t,1), centroid_traj(t,2), sprintf('t=%d', t), ... 'FontSize', 8, 'HorizontalAlignment', 'center'); end else text(0.5, 0.5, '无足够数据绘制质心轨迹', 'HorizontalAlignment', 'center', 'Units', 'normalized'); title('水舌质心运动轨迹'); end % 3. 最终时刻水舌分布 subplot(2, 3, 6); % 获取最后一个时间步的数据 last_start = integrated_data.time_step_markers(end, 1); last_end = integrated_data.time_step_markers(end, 2); last_coords = integrated_data.coordinates(last_start:last_end, :); last_volume_frac = integrated_data.volume_fraction(last_start:last_end); water_mask = last_volume_frac > 0.5; if any(water_mask) water_coords = last_coords(water_mask, :); scatter(water_coords(:,1), water_coords(:,2), 10, 'filled', 'MarkerFaceAlpha', 0.6); xlabel('X (m)'); ylabel('Y (m)'); title('最终时刻水舌分布'); grid on; else text(0.5, 0.5, '无足够数据绘制水舌分布', 'HorizontalAlignment', 'center', 'Units', 'normalized'); title('最终时刻水舌分布'); end % 保存图表和数据 fig_filename = sprintf('time_series_%s_analysis.fig', lower(analysis_type)); data_filename = sprintf('time_series_%s_data.mat', lower(analysis_type)); try saveas(fig, fig_filename); save(data_filename, 'integrated_data', 'integrated_results'); fprintf('图表已保存至:%s\n', fig_filename); fprintf('数据已保存至:%s\n', data_filename); catch ME fprintf('保存文件时出错:%s\n', ME.message); end % 关闭图形以释放内存 close(fig); end % ==================== 辅助函数 ==================== function all_datasets = get_all_h5_datasets(h5_info, current_path) % 递归获取H5文件中所有数据集的完整路径 if nargin < 2 current_path = ''; end all_datasets = {}; % 构建当前组的完整路径 if ~isempty(h5_info.Name) if isempty(current_path) current_group = ['/' h5_info.Name]; else current_group = [current_path '/' h5_info.Name]; end else current_group = current_path; end % 添加当前组中的数据集的完整路径 if isfield(h5_info, 'Datasets') && ~isempty(h5_info.Datasets) for i = 1:length(h5_info.Datasets) if isempty(current_group) full_path = ['/' h5_info.Datasets(i).Name]; else full_path = [current_group '/' h5_info.Datasets(i).Name]; end all_datasets{end+1} = full_path; %#ok<AGROW> end end % 递归处理子组 if isfield(h5_info, 'Groups') && ~isempty(h5_info.Groups) for i = 1:length(h5_info.Groups) sub_datasets = get_all_h5_datasets(h5_info.Groups(i), current_group); all_datasets = [all_datasets, sub_datasets]; %#ok<AGROW> end end end % ==================== 模拟数据生成函数 ==================== function generate_simulated_time_series() % 生成含.dat和.cas的模拟时间序列H5文件 n_time_steps = 8; fprintf('正在生成 %d 个模拟时间序列H5文件(包含.dat和.cas)...\n', n_time_steps); for t = 1:n_time_steps % 交替生成.dat和.cas文件 if mod(t, 2) == 1 file_name = sprintf('jet_data_%03d.dat.h5', t); else file_name = sprintf('jet_case_%03d.cas.h5', t); end n_nodes = 8000 + t * 500; coords = generate_simulated_coordinates(t); velocity = generate_simulated_velocity(n_nodes, t); pressure = generate_simulated_pressure(n_nodes, t); volume_fraction = generate_simulated_volume_fraction(n_nodes, t); % 确保目录存在 if exist(file_name, 'file') delete(file_name); end % 创建H5文件并写入数据 h5create(file_name, '/Mesh/Nodes', size(coords')); h5write(file_name, '/Mesh/Nodes', coords'); h5create(file_name, '/Flow/FieldData/Velocity', size(velocity')); h5write(file_name, '/Flow/FieldData/Velocity', velocity'); h5create(file_name, '/Flow/FieldData/Pressure', size(pressure)); h5write(file_name, '/Flow/FieldData/Pressure', pressure); h5create(file_name, '/Flow/FieldData/VolumeFraction', size(volume_fraction)); h5write(file_name, '/Flow/FieldData/VolumeFraction', volume_fraction); fprintf(' 生成文件:%s\n', file_name); end fprintf('模拟时间序列文件生成完成!\n'); end function coords = generate_simulated_coordinates(varargin) if nargin == 0 t = 1; else t = varargin{1}; end n_nodes = 8000 + t * 500; coords = zeros(n_nodes, 3); x_offset = (t-1) * 5; x_base = linspace(x_offset, 70 + x_offset, n_nodes)'; y_base = 50 + 17 * (1 - exp(-(x_base - x_offset)/30)) + (t-1) * 0.5; z_base = linspace(0, 6 + (t-1)*0.3, n_nodes)'; coords(:,1) = x_base + randn(n_nodes, 1) * 2; coords(:,2) = y_base + randn(n_nodes, 1) * 1; coords(:,3) = z_base + randn(n_nodes, 1) * 0.5; coords(:,1) = max(x_offset, min(70 + x_offset, coords(:,1))); coords(:,2) = max(50, min(67 + t, coords(:,2))); coords(:,3) = max(0, min(6 + t, coords(:,3))); end function velocity = generate_simulated_velocity(n_nodes, varargin) if nargin < 2 t = 1; else t = varargin{1}; end velocity = zeros(n_nodes, 3); speed_factor = 1 + (t-1)*0.05; base_speed = 25 * speed_factor + randn(n_nodes, 1) * 3; velocity(:,1) = base_speed .* (0.8 + 0.4 * rand(n_nodes, 1)); velocity(:,2) = randn(n_nodes, 1) * 2; velocity(:,3) = randn(n_nodes, 1) * 1; speed = sqrt(sum(velocity.^2, 2)); speed = max(20 * speed_factor, min(35 * speed_factor, speed)); velocity = velocity ./ (speed + eps) .* speed; end function pressure = generate_simulated_pressure(n_nodes, varargin) if nargin < 2 t = 1; else t = varargin{1}; end pressure_offset = (t-1) * 1000; pressure = 101325 + pressure_offset + randn(n_nodes, 1) * 5000; pressure = max(90000 + pressure_offset, min(110000 + pressure_offset, pressure)); end function volume_fraction = generate_simulated_volume_fraction(n_nodes, varargin) if nargin < 2 t = 1; else t = varargin{1}; end volume_fraction = zeros(n_nodes, 1); water_ratio = 0.6 + (t-1)*0.03; water_ratio = min(0.9, water_ratio); water_indices = 1:floor(n_nodes * water_ratio); volume_fraction(water_indices) = 0.9 + rand(length(water_indices), 1) * 0.1; air_indices = (floor(n_nodes * water_ratio) + 1):n_nodes; volume_fraction(air_indices) = rand(length(air_indices), 1) * 0.1; volume_fraction = min(1, max(0, volume_fraction)); end 这串代码怎么样,能实现吗,哪里需要改进
最新发布
11-25
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值