MATLAB GUI制作——转换mdf/mf4格式为mat格式
前言: 作为一名汽车软件开发或测试工程师,经常会碰到各种分析困难的问题,这就需要将测试数据导出至simulink中进行mil仿真,看看到底是哪里出了问题?Canape软件支持将mdf或mf4格式数据转换为mat格式的数据,但对于采集的长时间数据在导出过程中,想要保持一定的采样精度和连续性,经常会碰到导出报错“内存不足”、导出数据失败。这就需要想另外的办法,怎样保持采样精度也能保证连续性?本文将介绍怎样将mdf/mf4格式的数据保持采样精度和连续性导出为mat格式。
准备工具: MATLAB 2016B、CANape。
正文: 如果需要将mdf/mf4格式数据保存为mat格式,我们需要知道mdf/mf4格式文件的数据结构是什么样的?MATLAB 2016B中针对mdf/mf4格式文件有一个专门的函数可用于打开此文件,函数名为“mdf( )”。本文以CANape软件示例的一个mdf文件为例,首先用CANape打开一个mdf文件,查看文件有哪些信号?示例文件在CANape安装目录下,如本机为:D:\Program Files (x86)\Vector\Examples\Example\sinus.mdf。
查看信号SinusPhase0,就是一个正弦波。
查看完信号列表后,再用MATLAB打开对应的文件,看看在哪些地方能找到对应的信号。
打开后,看到文件的数据结构如下,在ChannelNames中能找到对应的信号名。
通过read( )函数读取对应信号,并使用plot( )函数画图,发现信号与CANape中曲线能完美对应上。
了解了mdf数据文件结构后,我们就开始使用脚本实现自动化读取想要的信号了。
制作完成的GUI界面如下:
通过如下代码,可以实现打开并读取mdf数据文件。
[FileName, PathName] = uigetfile('*.mf4;*.mdf', '选择一个mdf或mf4文件');
app.FilePath.Value = strcat(PathName, FileName);
app.DataFile = mdf(app.FilePath.Value);
在读取文件后,我们需要判断选取的文件是否是一个有效的mdf或mf4文件。
FileName = app.FilePath.Value;
FileNameLen = length(FileName);
if ~(strcmpi(FileName(FileNameLen-3:1:FileNameLen), '.mdf') || strcmpi(FileName(FileNameLen-3:1:FileNameLen), '.mf4'))
msgbox('无效mdf或mf4文件,请重新选择数据文件!');
end
读取并判断文件的有效性后,我们接下来就是读取所有需要的信号。我们将所有需要的信号以空格字符或“;”作为分隔符,将信号进行分割。如“SinusPhase0;SinusPhase90;SinusPhase180;SinusPhase270;”
%切割信号名序列
Variables = split(app.SignalName.Value, {' ',';'});
VariablesNum = length(Variables);
信号分割完成后,就是对各个信号进行罗列查找了。
VariableName(countFind) = read(app.DataFile, VariableNamei, char(Variables(i)), 'OutputFormat', 'timeseries');
为了了解到转换的进度,可通过添加数字进度,进行转换进度的展示。
%转换进度
percent = min(i/VariablesNum*100, 99);
percentStr = strcat(num2str(percent), '%');
app.StartMake.Text = strcat('转换进度:', percentStr);
所有信号查找完成后,需要将信号统一时间刻度,然后保存为mat格式。
eval(strcat(VariableName(i).Name, ' = interp1(VariableName(i).Time, VariableName(i).Data, t, ''linear'', ''extrap'');'));
save(char(DataName), VariableName(i).Name, '-append');
如果想要将所有缺失的信号展示出来,可通过如下代码实现。
if countMiss>=1
fid = fopen('missSignalName.txt','w');
fprintf(fid, '缺失的信号如下:');
fprintf(fid, '\n');
for i = 1:countMiss
fprintf(fid, char(Variables(missVariable(i))));
fprintf(fid, ';');
msgbox('该数据文件缺失部分信号,详见同目录下missSignalName.txt!');
end
通过以上主要的函数接口和代码,即可实现将mdf/mf4格式数据转换为mat格式。
如需具体代码,可留言联系。
应各位知友的要求,本帖将源代码贴上。
classdef CANape_DataFormat_Conversion < matlab.apps.AppBase
% Properties that correspond to app components
properties (Access = public)
mdftomatUIFigure matlab.ui.Figure
FilePath matlab.ui.control.EditField
mdfmf4Label matlab.ui.control.Label
SelectFile matlab.ui.control.Button
StartMake matlab.ui.control.Button
Label_2 matlab.ui.control.Label
SignalName matlab.ui.control.TextArea
Lamp matlab.ui.control.Lamp
Label_3 matlab.ui.control.Label
signalPeriod matlab.ui.control.NumericEditField
msLabel matlab.ui.control.Label
properties (Access = public)
DataFile; % Description
methods (Access = private)
% Callback function: FilePath, SelectFile
function SelectA2LFile(app, event)
[FileName, PathName] = uigetfile('*.mf4;*.mdf', '选择一个mdf或mf4文件');
app.FilePath.Value = strcat(PathName, FileName);
app.DataFile = mdf(app.FilePath.Value);
% Button pushed function: StartMake
function StartMakeButtonPushed(app, event)
app.StartMake.Enable = 'off';
FileName = app.FilePath.Value;
FileNameLen = length(FileName);
if ~(strcmpi(FileName(FileNameLen-3:1:FileNameLen), '.mdf') || strcmpi(FileName(FileNameLen-3:1:FileNameLen), '.mf4'))
msgbox('无效mdf或mf4文件,请重新选择数据文件!');
app.Lamp.Color = [1,0,0];
%设定待保存文件名
FileNameSplit = split(app.FilePath.Value, '\');
tempName = char(FileNameSplit(length(FileNameSplit)));
DataName = strcat(tempName(1:length(tempName)-4), '.mat');
%切割信号名序列
Variables = split(app.SignalName.Value, {' ',';'});
VariablesNum = length(Variables);
%逐个查找信号名并保存至文件
countMiss = 0;
countFind = 0;
for i = 1:VariablesNum
if strcmp(Variables(i), '')==0
flag = 0;
for VariableNamei = 1:length(app.DataFile.ChannelNames)
for VariableNamej = 1:length(app.DataFile.ChannelNames{VariableNamei})
if strcmp(Variables(i), app.DataFile.ChannelNames{VariableNamei}{VariableNamej})==1
flag = 1;
break;
if flag==1
break;
if VariableNamei==length(app.DataFile.ChannelNames) && flag==0
countMiss = countMiss + 1;
missVariable(countMiss) = i;
countFind = countFind + 1;
VariableName(countFind) = read(app.DataFile, VariableNamei, char(Variables(i)), 'OutputFormat', 'timeseries');
%转换进度
percent = min(i/VariablesNum*100, 99);
percentStr = strcat(num2str(percent), '%');
app.StartMake.Text = strcat('转换进度:', percentStr);
catch
countFind = countFind - 1;
countMiss = countMiss + 1;
missVariable(countMiss) = i;
% 数据统一时间
if countFind>0
if app.signalPeriod.Value/1000.0 > max(VariableName(1).Time)
msgbox('设定的信号周期超过了信号的最大时间,请重新设定合适的信号周期!');
return;
t = [0:app.signalPeriod.Value/1000.0:max(VariableName(1).Time)]';
save(char(DataName), 't');
for i = 1:countFind
eval(strcat(VariableName(i).Name, ' = interp1(VariableName(i).Time, VariableName(i).Data, t, ''linear'', ''extrap'');'));
save(char(DataName), VariableName(i).Name, '-append');
app.StartMake.Text = strcat('转换进度:', '100%');
if countMiss>=1
fid = fopen('missSignalName.txt','w');
fprintf(fid, '缺失的信号如下:');
fprintf(fid, '\n');
for i = 1:countMiss
fprintf(fid, char(Variables(missVariable(i))));
fprintf(fid, ';');
msgbox('该数据文件缺失部分信号,详见同目录下missSignalName.txt!');
msgbox('数据格式转换完成!');
app.StartMake.Text = '开始转换';
app.StartMake.Enable = 'on';
app.Lamp.Color = [0,1,0];
% App initialization and construction
methods (Access = private)
% Create UIFigure and components
function createComponents(app)
% Create mdftomatUIFigure
app.mdftomatUIFigure = uifigure;
app.mdftomatUIFigure.Position = [100 100 640 480];
app.mdftomatUIFigure.Name = 'mdf to mat';
app.mdftomatUIFigure.Resize = 'off';
setAutoResize(app, app.mdftomatUIFigure, true)
% Create FilePath
app.FilePath = uieditfield(app.mdftomatUIFigure, 'text');
app.FilePath.ValueChangedFcn = createCallbackFcn(app, @SelectA2LFile, true);
app.FilePath.Position = [169 437 370 22];
% Create mdfmf4Label
app.mdfmf4Label = uilabel(app.mdftomatUIFigure);
app.mdfmf4Label.HorizontalAlignment = 'right';
app.mdfmf4Label.FontWeight = 'bold';
app.mdfmf4Label.Position = [17 441 153 15];
app.mdfmf4Label.Text = '待处理的mdf或mf4文件:';
% Create SelectFile
app.SelectFile = uibutton(app.mdftomatUIFigure, 'push');
app.SelectFile.ButtonPushedFcn = createCallbackFcn(app, @SelectA2LFile, true);
app.SelectFile.FontWeight = 'bold';
app.SelectFile.Position = [558 437 69 22];
app.SelectFile.Text = '选择';
% Create StartMake
app.StartMake = uibutton(app.mdftomatUIFigure, 'push');
app.StartMake.ButtonPushedFcn = createCallbackFcn(app, @StartMakeButtonPushed, true);
app.StartMake.FontSize = 14;
app.StartMake.FontWeight = 'bold';
app.StartMake.Position = [152 22 432 32];
app.StartMake.Text = '开始转换';
% Create Label_2
app.Label_2 = uilabel(app.mdftomatUIFigure);
app.Label_2.HorizontalAlignment = 'right';
app.Label_2.FontWeight = 'bold';
app.Label_2.Position = [17 402 109 15];
app.Label_2.Text = '待转换的信号名:';
% Create SignalName
app.SignalName = uitextarea(app.mdftomatUIFigure);
app.SignalName.Position = [27 70 600 323];
% Create Lamp
app.Lamp = uilamp(app.mdftomatUIFigure);
app.Lamp.Position = [596 28 20 20];
% Create Label_3
app.Label_3 = uilabel(app.mdftomatUIFigure);
app.Label_3.HorizontalAlignment = 'right';
app.Label_3.FontWeight = 'bold';
app.Label_3.Position = [17 31 57 15];
app.Label_3.Text = '信号周期';
% Create signalPeriod
app.signalPeriod = uieditfield(app.mdftomatUIFigure, 'numeric');
app.signalPeriod.Limits = [1 10000];
app.signalPeriod.Position = [76 27 37 22];
app.signalPeriod.Value = 5;
% Create msLabel
app.msLabel = uilabel(app.mdftomatUIFigure);
app.msLabel.Position = [116 31 25 15];
app.msLabel.Text = 'ms';
methods (Access = public)
% Construct app
function app = CANape_DataFormat_Conversion()
% Create and configure components
createComponents(app)
% Register the app with App Designer
registerApp(app, app.mdftomatUIFigure)
if nargout == 0
clear app
% Code that executes before app deletion
function delete(app)