% voltageDataProcessor_complete.m - 完整的电压数据实时处理与显示程序
function voltageDataProcessor_complete()
% 选择输入文件
[fileName, filePath] = uigetfile('*.dat', '选择DAT文件');
if fileName == 0
fprintf('未选择文件,程序退出。\n');
return;
end
% 完整文件路径
fullPath = fullfile(filePath, fileName);
% 创建图形窗口 - 定义 fig 变量
fig = figure('Name', '电压数据实时处理与显示', ...
'Position', [100, 100, 1000, 700], ...
'CloseRequestFcn', @closeFigureCallback, ...
'Color', [0.95, 0.95, 0.95]);
% 创建3个子图(垂直排列)
ax(1) = subplot(3, 1, 1);
p1 = plot(ax(1), NaN, NaN, 'b');
title('电压检测值1 (天线)');
xlabel('采样点');
ylabel('电压(V)');
grid on;
ax(2) = subplot(3, 1, 2);
p2 = plot(ax(2), NaN, NaN, 'g');
title('电压检测值2 (中频放大器)');
xlabel('采样点');
ylabel('电压(V)');
grid on;
ax(3) = subplot(3, 1, 3);
p3 = plot(ax(3), NaN, NaN, 'r');
title('电压检测值3 (低频放大器)');
xlabel('采样点');
ylabel('电压(V)');
grid on;
% 初始化数据存储变量
voltage1 = [];
voltage2 = [];
voltage3 = [];
% 文件读取状态
fileID = -1;
lastPos = 0;
% 滤波参数设置
filterOrder = 4; % 滤波器阶数
cutoffFreq = 0.1; % 截止频率(归一化)
% 创建低通滤波器
[b, a] = butter(filterOrder, cutoffFreq, 'low');
% 创建状态面板 - 使用已定义的 fig 变量
statusPanel = uipanel(fig, 'Title', '系统状态', ... % 使用 fig 句柄
'Position', [0.02, 0.02, 0.96, 0.15], ...
'BackgroundColor', [0.95, 0.95, 0.95]);
% 添加天线状态文本
antennaFaultText = uicontrol(statusPanel, 'Style', 'text', ...
'Position', [20, 80, 200, 20], ...
'String', '天线: 正常', ...
'FontSize', 10, ...
'FontWeight', 'bold', ...
'BackgroundColor', [0.95, 0.95, 0.95], ...
'ForegroundColor', [0, 0.6, 0], ...
'HorizontalAlignment', 'left');
% 状态文本
statusText = uicontrol(statusPanel, 'Style', 'text', ...
'Position', [20, 20, 600, 20], ...
'String', '准备开始...', ...
'FontSize', 10, ...
'BackgroundColor', [0.95, 0.95, 0.95], ...
'HorizontalAlignment', 'left');
freqText = uicontrol(statusPanel, 'Style', 'text', ...
'Position', [20, 50, 300, 20], ...
'String', '频率: 计算中...', ...
'FontSize', 10, ...
'BackgroundColor', [0.95, 0.95, 0.95], ...
'HorizontalAlignment', 'left');
% 中频和低频故障文本
midFaultText = uicontrol(statusPanel, 'Style', 'text', ...
'Position', [350, 80, 200, 20], ...
'String', '中频放大器: 正常', ...
'FontSize', 10, ...
'FontWeight', 'bold', ...
'BackgroundColor', [0.95, 0.95, 0.95], ...
'ForegroundColor', [0, 0.6, 0], ...
'HorizontalAlignment', 'left');
lowFaultText = uicontrol(statusPanel, 'Style', 'text', ...
'Position', [350, 50, 200, 20], ...
'String', '低频放大器: 正常', ...
'FontSize', 10, ...
'FontWeight', 'bold', ...
'BackgroundColor', [0.95, 0.95, 0.95], ...
'ForegroundColor', [0, 0.6, 0], ...
'HorizontalAlignment', 'left');
thresholdText = uicontrol(statusPanel, 'Style', 'text', ...
'Position', [600, 20, 350, 60], ...
'String', sprintf('阈值设置:\n天线故障: 连续两次Δ>0.28V\n天线恢复: 一次Δ>0.3V\n中频故障: <1.9V 正常: >1.95V\n低频故障: >1.72V 正常: <1.6V\n中频故障时低频显示正常'), ...
'FontSize', 9, ...
'BackgroundColor', [0.95, 0.95, 0.95], ...
'HorizontalAlignment', 'left');
% 停止按钮
stopBtn = uicontrol(statusPanel, 'Style', 'pushbutton', ...
'Position', [600, 80, 100, 30], ...
'String', '停止监控', ...
'Callback', @stopMonitoring, ...
'FontSize', 10, ...
'BackgroundColor', [0.9, 0.4, 0.4]);
% 存储应用数据
appData = struct(...
'voltage1', voltage1, ...
'voltage2', voltage2, ...
'voltage3', voltage3, ...
'fileID', -1, ...
'lastPos', 0, ...
'fullPath', fullPath, ...
'running', true, ...
'p1', p1, ...
'p2', p2, ...
'p3', p3, ...
'ax', ax, ...
'statusText', statusText, ...
'freqText', freqText, ...
'antennaFaultText', antennaFaultText, ...
'midFaultText', midFaultText, ...
'lowFaultText', lowFaultText, ...
'b', b, ...
'a', a, ...
'maxDataPoints', 10000, ...
'maxDisplayPoints', 1000, ...
'antennaFaultState', 0, ... % 天线状态机 (0:正常, 1:第一次超差, 2:故障)
'antennaFaultStatus', false, ... % 天线当前故障状态
'midFaultStatus', false, ... % 中频放大器故障状态
'lowFaultStatus', false, ... % 低频放大器故障状态
'previousAntennaStatus', false, ...% 上一次天线状态
'previousMidStatus', false, ... % 上一次中频状态
'previousLowStatus', false ... % 上一次低频状态
);
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)
appData = getappdata(fig, 'appData');
set(appData.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 = [];
for i = 1:length(newLines)
line = strtrim(newLines{i});
% 跳过空行
if isempty(line)
continue;
end
% 提取数字
[num, success] = extractNumber(line);
if success
% 根据行号分配到对应的电压数组
switch mod(i-1, 3) + 1
case 1
newVoltage1 = [newVoltage1; num];
case 2
newVoltage2 = [newVoltage2; num];
case 3
newVoltage3 = [newVoltage3; num];
end
end
end
% 更新数据
if ~isempty(newVoltage1)
appData.voltage1 = [appData.voltage1; newVoltage1];
appData.voltage2 = [appData.voltage2; newVoltage2];
appData.voltage3 = [appData.voltage3; newVoltage3];
% 限制数据缓冲区大小
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);
end
% 更新图形
updatePlot(appData.p1, appData.voltage1, appData.maxDisplayPoints);
updatePlot(appData.p2, appData.voltage2, appData.maxDisplayPoints);
updatePlot(appData.p3, appData.voltage3, appData.maxDisplayPoints);
% 计算并显示通道3的频率
if length(appData.voltage3) > 100
fs = 100; % 假设采样频率为100Hz
frequency = calculateFrequency(appData.voltage3, fs);
set(appData.freqText, 'String', sprintf('频率: %.2f Hz', frequency));
title(appData.ax(3), sprintf('电压检测值3 (主要频率: %.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);
end
% ====== 故障检测逻辑 ======
% 保存之前的状态
appData.previousAntennaStatus = appData.antennaFaultStatus;
appData.previousMidStatus = appData.midFaultStatus;
appData.previousLowStatus = appData.lowFaultStatus;
% 1. 天线故障检测(电压检测值1)
if ~isempty(appData.voltage1) && length(appData.voltage1) >= 2
% 获取最近两个电压值
lastValue1 = appData.voltage1(end);
prevValue1 = appData.voltage1(end-1);
% 计算变化量
delta = abs(lastValue1 - prevValue1);
% 状态机实现
switch appData.antennaFaultState
case 0 % 正常状态
if delta > 0.28
appData.antennaFaultState = 1; % 进入第一次超差状态
end
case 1 % 第一次超差
if delta > 0.28
appData.antennaFaultState = 2; % 进入故障状态
appData.antennaFaultStatus = true;
else
appData.antennaFaultState = 0; % 返回正常
end
case 2 % 故障状态
if delta > 0.3
appData.antennaFaultState = 0; % 返回正常
appData.antennaFaultStatus = false;
end
end
% 更新天线状态显示
if appData.antennaFaultStatus
set(appData.antennaFaultText, 'String', '天线: 故障', 'ForegroundColor', [1, 0, 0]);
else
set(appData.antennaFaultText, 'String', '天线: 正常', 'ForegroundColor', [0, 0.6, 0]);
end
end
% 2. 中频放大器故障检测(电压检测值2)
if ~isempty(appData.voltage2)
lastValue2 = appData.voltage2(end);
if lastValue2 < 1.9 % 故障条件
set(appData.midFaultText, 'String', '中频放大器: 故障', 'ForegroundColor', [1, 0, 0]);
appData.midFaultStatus = true;
elseif lastValue2 > 1.95 % 正常条件
set(appData.midFaultText, 'String', '中频放大器: 正常', 'ForegroundColor', [0, 0.6, 0]);
appData.midFaultStatus = false;
end
% 1.9V-1.95V之间保持原状态
end
% 3. 低频放大器故障检测(电压检测值3)
if ~isempty(appData.voltage3)
lastValue3 = appData.voltage3(end);
% 中频故障时强制显示低频正常
if appData.midFaultStatus
set(appData.lowFaultText, 'String', '低频放大器: 正常', 'ForegroundColor', [0, 0.6, 0]);
appData.lowFaultStatus = false;
else
% 正常检测逻辑
if lastValue3 > 1.72 % 故障条件
set(appData.lowFaultText, 'String', '低频放大器: 故障', 'ForegroundColor', [1, 0, 0]);
appData.lowFaultStatus = true;
elseif lastValue3 < 1.6 % 正常条件
set(appData.lowFaultText, 'String', '低频放大器: 正常', 'ForegroundColor', [0, 0.6, 0]);
appData.lowFaultStatus = false;
end
% 1.6V-1.72V之间保持原状态
end
end
% 检查状态变化,触发报警
faultDetected = false;
faultMessage = '';
if ~appData.previousAntennaStatus && appData.antennaFaultStatus
faultDetected = true;
faultMessage = '检测到天线故障!';
end
if ~appData.previousMidStatus && appData.midFaultStatus
faultDetected = true;
faultMessage = [faultMessage ' 检测到中频放大器故障!'];
end
if ~appData.previousLowStatus && appData.lowFaultStatus
faultDetected = true;
faultMessage = [faultMessage ' 检测到低频放大器故障!'];
end
if faultDetected
playBeep();
set(appData.statusText, 'String', strtrim(faultMessage));
elseif ~appData.antennaFaultStatus && ~appData.midFaultStatus && ~appData.lowFaultStatus
set(appData.statusText, 'String', '系统运行正常');
end
% 更新全局状态
setappdata(figHandle, 'appData', appData);
% ====== 故障检测结束 ======
% 更新文件位置
appData.lastPos = currentFileSize;
setappdata(figHandle, 'appData', appData);
end
end
% 刷新图形
drawnow limitrate;
catch ME
appData = getappdata(figHandle, 'appData');
set(appData.statusText, 'String', ['错误: ' ME.message]);
appData.running = false;
setappdata(figHandle, 'appData', appData);
end
end
% 蜂鸣声函数
function playBeep()
% 创建一个短暂的音频信号
fs = 8192; % 采样率
t = 0:1/fs:0.2; % 0.2秒
f = 1000; % 音调频率
beepSignal = sin(2*pi*f*t);
% 播放声音(使用soundsc自动调整幅度)
soundsc(beepSignal, fs);
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
% 应用滤波
if length(data) > 10
filteredData = filtfilt(b, a, data);
else
filteredData = data;
end
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
帮我修改代码,要求如下当电压检测值1数据低于1.8时,判定为电源故障,数据高于2时,判断为电源正常。
当电压检测值2出现数据低于1.9V时,判定为中频放大器故障。当电压检测值2出现数据高于2V时判定为中频放大器正常
当电压检测值3出现数据高于1.72V时,判定为低频放大器故障。当电压检测值3出现数据低于1.6V时,判定为低频放大器正常。如果判定为中频放大器故障,低频放大器就不能判定为故障,显示为低频放大器正常。如果没故障时界面分别显示绿色的中频放大器正常和低频放大器正常。如果发生故障,蜂鸣器响一声提示故障具体的故障点文字变化为红色。