├── ExportMDF.mlappinstall ├── .gitignore ├── LICENSE ├── README.md ├── expMDF.m └── mat2dat.m /ExportMDF.mlappinstall: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rrahuldev/ExportMDF/HEAD/ExportMDF.mlappinstall -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.mat 2 | *.dat 3 | *.prj 4 | unittest.m 5 | *.mdl 6 | sampleData** 7 | pushUpdates.sh 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Rahul Rajampeta 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ExportMDF 2 | Converts Matlab/Simulink data saved in workspace or 'MAT' file to DAT format (MDF 3.0) which can be read by ETAS MDA(INCA) or CANape tool. 3 | Currently, script works using Matlab libraries version 8.2 (R2013b) or higher. 4 | 5 | # Usage 6 | For use with Matlab App 7 | - 8 | 1) The script uses simulation data saved to MAT file. First use either of the following ways to save simulation data to MAT file - 9 | 10 | - Use 'to workspace' Simulink block for data logging respective signals (use option "Save format" as "Timeseries") & in the workspace select the variable names generated after simulation to create the MAT file. 11 | - Use 'ToFile' Simulink block with 'bus' block to write multiple signals to same *.mat file (use option "Save format" as "Timeseries") 12 | - Export simulation data using signal logging (check link: https://www.mathworks.com/help/simulink/ug/exporting-signal-data-using-signal-logging.html). Use the simulation output variable of signal logging (eg. logsout) stored as "Simulink.SimulationData.Dataset" datatype and create a MAT file. 13 | 14 | 2) Install the "ExportMDF" app and use the app to select the MAT file. 15 | 16 | 3) Output file is stored in the same location as input mat file. 17 | 18 | For use with commandline parameters 19 | - 20 | First export simulation data using signal logging (check link: https://www.mathworks.com/help/simulink/ug/exporting-signal-data-using-signal-logging.html). 21 | 22 | Then use the simulation output variable of signal logging (eg. logsout) stored as "Simulink.SimulationData.Dataset" datatype with the script. Eg.- mat2dat(logsout, 'target_filename.dat') 23 | 24 | For inline, script uses two parameters; 25 | - param1 - exported simulation data stored as "Simulink.SimulationData.Dataset" datatype 26 | - param2 - target filename 27 | -------------------------------------------------------------------------------- /expMDF.m: -------------------------------------------------------------------------------- 1 | % Write data to MDA dat file 2 | % inputs: 3 | % 1- Data in table format 4 | % 2 - filename to write to *.dat 5 | % 6 | % usage: expMDF(dataTable, fileoutname) 7 | 8 | % Author : Rahul Rajampeta 9 | % Date: April 15, 2016 10 | 11 | function expMDF(dataTable, fileoutname) 12 | %% create the fid to write 13 | fid = fopen(fileoutname,'W'); 14 | 15 | %% pass the table data 16 | varNames = getVariableNames(dataTable); 17 | varUnits = getTableUnits(dataTable); 18 | numChannels = length(varNames); 19 | numSamples = height(dataTable); 20 | rateSample = 0; % required for virtual signal only. 21 | 22 | %% Define locations for each block 23 | linkID = 0; % DO NOT CHANGE THIS 24 | linkHD = 64; % DO NOT CHANGE THIS 25 | linkCC = 300; 26 | blocksize_cc = 63; % for linear format of data 63 bytes 27 | padd_cc = 2; 28 | linkCN = linkCC + numChannels*(blocksize_cc+padd_cc); 29 | blocksize_cn = 228; % constant 30 | padd_cn = 2; 31 | linkCG = linkCN + numChannels*(blocksize_cn+padd_cn); 32 | linkDG = linkCG + 50; % 26+padding 33 | linkDT = linkDG + 50; %28 + padding 34 | 35 | % NOTE: dump empty characters untill linkDT otherwise fseek wont move to 36 | % desired position to write to 37 | fwrite(fid, repmat(char(0),1,linkDT), 'char'); 38 | 39 | %% write ID block 40 | writeIDBlock(fid, linkID); 41 | 42 | %% write HD block 43 | writeHDBlock(fid, linkHD, linkDG); 44 | 45 | %% write DG block - assume only one DG 46 | writeDGBlock(fid, linkDG, linkCG, linkDT); 47 | 48 | %% write CG block - assume only one CG 49 | writeCGBlock(fid, linkCG, linkCN, numChannels, numSamples); 50 | 51 | %% write CC/CN blocks 52 | for vars = 1 : numChannels 53 | writeCCBlock(fid, linkCC, varUnits{vars}); 54 | nextCN = linkCN+blocksize_cn+padd_cn; 55 | writeCNBlock(fid, linkCN, linkCC, nextCN, varNames{vars}, vars, rateSample, vars == numChannels); 56 | 57 | linkCC = linkCC+blocksize_cc+padd_cc; 58 | linkCN = linkCN+blocksize_cn+padd_cn; 59 | end 60 | 61 | %% and write DTblock 62 | writeDTBlock(fid, linkDT, dataTable); 63 | 64 | fclose(fid); 65 | end 66 | 67 | %% ID block def 68 | % dont change this 69 | function writeIDBlock(fid, offset) 70 | fseek(fid,offset,'bof'); 71 | fwrite(fid,'MDF 3.00 TGT 15.0', 'char'); 72 | fwrite(fid,0,'uint16'); 73 | fwrite(fid,0,'uint16'); 74 | fwrite(fid,300,'uint16'); 75 | fwrite(fid,0,'uint16'); 76 | fwrite(fid,repmat(char(0),1,28),'char'); 77 | fwrite(fid,0,'uint16'); 78 | fwrite(fid,0,'uint16'); 79 | % ftell(fid) 80 | end 81 | 82 | %% HD block def 83 | function writeHDBlock(fid, offset, linkDG) 84 | blocksize = 164; 85 | 86 | fseek(fid,offset,'bof'); % file pointer always to 64 for HD 87 | fwrite(fid,char('HD'),'char'); 88 | fwrite(fid,blocksize,'uint16'); 89 | fwrite(fid,linkDG,'uint32'); % pointer to DG block 90 | fwrite(fid,0,'uint32'); % pointer to TX 91 | fwrite(fid,0,'uint32'); %pointer to PR 92 | fwrite(fid,1,'uint16'); % number of DG BLOCKS (assume only one to write to) 93 | fwrite(fid,datestr(now,'dd:mm:yyyy'),'char'); % datestring 94 | fwrite(fid,datestr(now,'HH:MM:SS'),'char'); % timestring 95 | username = getenv('USERNAME'); 96 | fwrite(fid,username(1:min(length(username),31)), 'char'); % author 97 | fseek(fid,32-length(username),0); % fill the rest 32bytes with null 98 | fwrite(fid,repmat(char(' '),1,32),'char'); %organisation name 99 | fwrite(fid,repmat(char(' '),1,32),'char'); % project name 100 | fwrite(fid,repmat(char(' '),1,32),'char'); % vehicle/dyno name 101 | end 102 | 103 | %% DG block def 104 | function writeDGBlock(fid, offset, linkCG, linkDT) 105 | %assume only one DG block to write 106 | blocksize = 28; 107 | 108 | fseek(fid,offset,'bof'); 109 | fwrite(fid,char('DG'),'2*char'); 110 | fwrite(fid,blocksize,'uint16'); %block size 111 | fwrite(fid,0,'uint32'); %skip next DG 112 | fwrite(fid,linkCG,'uint32'); % CG link 113 | fwrite(fid,0,'uint32'); % skip trigger block TR 114 | fwrite(fid,linkDT,'uint32'); % Data DR link 115 | fwrite(fid,1,'uint16'); % assume only one CG block 116 | fwrite(fid,0,'uint16'); % assume zero recordIDs 117 | fwrite(fid,0,'uint32'); %reserved 118 | if blocksize ~= (ftell(fid)-offset) 119 | disp(['blocksize error DG']); 120 | end 121 | end 122 | 123 | %% CG block def 124 | function writeCGBlock(fid, offset, linkCN, numChannels, numSamples) 125 | %assume only one CG block to write 126 | blocksize = 26; 127 | 128 | fseek(fid,offset,'bof'); 129 | fwrite(fid,char('CG'),'2*char'); 130 | fwrite(fid,blocksize,'uint16'); %block size 131 | fwrite(fid,0,'uint32'); %skip next CG 132 | fwrite(fid,linkCN,'uint32'); %link to CN block 133 | fwrite(fid,0,'uint32'); %skip TX block 134 | fwrite(fid,2,'uint16'); %recordID 135 | fwrite(fid,uint16(numChannels),'uint16'); % number of channels 136 | fwrite(fid,uint16(numChannels*4),'uint16'); % record size (number of channels*(single)=32bits=4bytes) 137 | fwrite(fid,uint32(numSamples),'uint32'); %number of samples 138 | if blocksize ~= (ftell(fid)-offset) 139 | disp(['blocksize error CG']); 140 | end 141 | 142 | end 143 | 144 | %% CN block def 145 | function writeCNBlock(fid, offset, linkCC, nextLinkCN, signalName, numVar, rateSample, lastVar) 146 | if lastVar 147 | nextLinkCN = 0; 148 | end 149 | blocksize = 228; 150 | 151 | % calculate start offset with byte offset 152 | % if start bit is more than 16 bytes, use additional byte offset 153 | start_bit_raw = 32*(numVar-1); 154 | if (start_bit_raw <= 2^16-1) 155 | start_offset = start_bit_raw; 156 | add_byte_offset = 0; 157 | else 158 | start_offset = mod(start_bit_raw, 8); 159 | add_byte_offset = floor(start_bit_raw/8); 160 | end 161 | 162 | fseek(fid,offset,'bof'); 163 | fwrite(fid,'CN','char'); 164 | fwrite(fid,blocksize,'uint16'); %block size 165 | fwrite(fid,nextLinkCN,'uint32'); % link to nextCN 166 | fwrite(fid,linkCC,'uint32'); % link to CC block 167 | fwrite(fid,0,'uint32'); % link to CE block 168 | fwrite(fid,0,'uint32'); % link to CD block 169 | fwrite(fid,0,'uint32'); % link to TX block 170 | if strcmp(signalName,'time') 171 | fwrite(fid,1,'uint16'); %channel type 172 | else 173 | fwrite(fid,0,'uint16'); 174 | end 175 | fwrite(fid,signalName(1:min(length(signalName),31)), 'char'); 176 | fseek(fid,32-length(signalName),0); % fill the rest 32bytes with null 177 | fwrite(fid,'simulink output data','char'); 178 | fseek(fid,128-length('simulink output data'),0); % fill the rest 128bytes with null 179 | fwrite(fid,start_offset,'uint16'); %start offset 180 | fwrite(fid,32,'uint16'); %num of bits 181 | fwrite(fid,2,'uint16'); % single format (4bytes) 182 | fwrite(fid,0,'uint16'); % value range valid 183 | fwrite(fid,0,'double'); % min signal value 184 | fwrite(fid,0,'double'); % max signal value 185 | fwrite(fid,rateSample,'double'); % sampling rate 186 | fwrite(fid,0,'uint32'); %skip TX block 187 | fwrite(fid,0,'uint32'); %skip TX block 188 | fwrite(fid,add_byte_offset,'uint16'); % additional byte offset 189 | end 190 | 191 | %% CC block def 192 | function writeCCBlock(fid, offset, units) 193 | % units modification - limit to 20 with trailing zeros 194 | units = units(1:min(19,length(units))); 195 | units = [units repmat(char(0),1,20-length(units))]; 196 | 197 | blocksize = 62; 198 | 199 | fseek(fid,offset,'bof'); 200 | fwrite(fid,'CC','char'); 201 | fwrite(fid,blocksize,'uint16'); %block size 202 | fwrite(fid,0,'uint16'); % value range valid 203 | fwrite(fid,0,'double'); % min signal value 204 | fwrite(fid,0,'double'); % max signal value 205 | fwrite(fid,units,'char'); % units of the signal 206 | fwrite(fid,0,'uint16'); % conversion type - linear 207 | fwrite(fid,2,'uint16'); % number of parameters 208 | fwrite(fid,0,'double'); % P1 209 | fwrite(fid,1,'double'); % P2 210 | 211 | end 212 | 213 | %% Data block - write row-wise 214 | function writeDTBlock(fid, offset, dataTable) 215 | fseek(fid,offset,'bof'); 216 | % for row = 1:height(dataTable) 217 | % fwrite(fid,dataTable{row,:},'single'); 218 | % end 219 | dataTableRow = reshape(dataTable{:,:}', 1, []); 220 | fwrite(fid,dataTableRow, 'single'); 221 | end 222 | 223 | %% Fix variable names 224 | function varnames = getVariableNames(dataTable) 225 | % variable names should conform to 226 | % 1 - no space allowed : replace with __ 227 | % 2 - length should be less than 31 chars 228 | % 3 - unique name for each variables 229 | varnames = dataTable.Properties.VariableNames; 230 | 231 | % check if space exists in names 232 | varnames = strrep(varnames, ' ','_'); 233 | 234 | % check more than 31 chars names 235 | flg_longnames = cellfun(@(x) length(x)>31, varnames, 'UniformOutput', false); 236 | flg_longnames = [flg_longnames{:}]'; % convert cell to logical array 237 | % then truncate in between; use rolling 31 chars that produce unique names 238 | if any(flg_longnames) 239 | newvarnames = varnames; 240 | longnames = varnames(flg_longnames); 241 | shift_idx=[0:1:14 -1:-1:-14]; 242 | i=1; 243 | while ((length(unique(newvarnames)) ~= length(newvarnames) || i==1) && i<= length(shift_idx)) 244 | idx = shift_idx(i); 245 | newlongnames = cellfun(@(x) [x(1:15-idx) '_' x(end-14-idx:end)], longnames, 'UniformOutput', false); 246 | newvarnames(flg_longnames) = newlongnames; 247 | i=i+1; 248 | end 249 | if (length(unique(newvarnames)) ~= length(newvarnames) && i > length(shift_idx)) 250 | error('Try to minimize the signal names to less than 31 chars'); 251 | else 252 | varnames = newvarnames; 253 | end 254 | end 255 | 256 | % convert ls/rs to brackets 257 | varnames = strrep(varnames, '_ls_','['); 258 | varnames = strrep(varnames, '_rs_',']'); 259 | 260 | end 261 | 262 | %% DataTable units 263 | function units = getTableUnits(dataTable) 264 | units = dataTable.Properties.VariableUnits; 265 | 266 | % if no units are set to either of the column 267 | if isempty(units) 268 | % set empty units to first column, it forces empty units to all columns 269 | dataTable.Properties.VariableUnits{1} = ''; 270 | units = dataTable.Properties.VariableUnits; 271 | end 272 | end 273 | -------------------------------------------------------------------------------- /mat2dat.m: -------------------------------------------------------------------------------- 1 | function mat2dat(varargin) 2 | % The script is invoked two ways 3 | % 1: with no arguments to the script, in which case, the user selects the 4 | % one or more mat files to convert to dat file format. The output is the 5 | % same name as matfile but appended with "_expMDF.dat" 6 | % 7 | % 2: inline with other scripts or model where the syntax below is followed. 8 | % usage: MAT2DAT(logsout, 'test_data.dat') 9 | % params: 10 | % logsout: Simulink.SimulationData.Dataset type data stored via simulation output 11 | % 'test_data.dat' : filename to convert data to dat 12 | % returns: 13 | % 'test_data.dat': data converted to MDF format 14 | % simulation model call; assume logsout variable in the workspace 15 | 16 | % Author: Rahul Rajampeta 17 | % Updated: April 11, 2019 18 | 19 | disp(' '); 20 | %% check matlab version : currently supports 8.2 and higher 21 | if verLessThan('matlab', '8.2') 22 | disp(repmat(char('-'), 1, 90)); 23 | disp(' Currently, this script supports only version 8.2 (R2013b) and higher '); 24 | disp(repmat(char('-'), 1, 90)); 25 | disp(' '); 26 | return; 27 | end 28 | 29 | %% check invoke method of the script 30 | switch nargin 31 | case 0 32 | % Select mat file with variables stored as timeseries 33 | % UI for file selection 34 | [FileName,PathName,~] = uigetfile(fullfile(pwd,'*.mat'), 'Select the MAT file(s). Use Ctrl key for selecting multiple files','MultiSelect','on'); 35 | 36 | % check one or multiple files selected 37 | if isa(FileName, 'char') 38 | % only one file selected 39 | % convert to cell format 40 | FileName = {FileName}; 41 | elseif ~isa(FileName, 'cell') 42 | % no file selected - end the function call 43 | return 44 | end 45 | 46 | % load each file and create dat file for each 47 | for f = 1: length(FileName) 48 | disp('');disp(['Loading file -- "', FileName{f},'"']); 49 | raw = load(strcat(PathName,FileName{f})); 50 | writeDatFile(raw, FileName{f}, PathName); 51 | end 52 | 53 | case 2 54 | % usage: mat2dat(logsout, 'test_data.dat') 55 | % params: 56 | % logsout: Simulink.SimulationData.Dataset type data stored via simulation output 57 | % 'test_data.dat' : filename to convert data to dat 58 | % returns: 59 | % 'test_data.dat': data converted to MDF format 60 | % simulation model call; assume logsout variable in the workspace 61 | 62 | dataStream = varargin{1}; 63 | filename=varargin{2}; 64 | if isa(dataStream, 'Simulink.SimulationData.Dataset') 65 | rawTSData = classSimDataset(dataStream); 66 | writeDatFile(rawTSData, filename, strcat(pwd,'\')); 67 | else 68 | disp('ERROR: Pass Simulation dataset values only') 69 | end 70 | end 71 | end 72 | 73 | function writeDatFile(rawStructure, FileName, PathName) 74 | %% pass raw structure to preprocessing 75 | dataStruct = classStruct(rawStructure); 76 | try 77 | dataTable = struct2table(dataStruct); 78 | catch Error 79 | disp(' ') 80 | disp(' *** ERROR IN MAT FILE *** '); 81 | disp(Error.message); 82 | disp(' --> Check MAT file, all signals must have same length'); 83 | return 84 | end 85 | 86 | %% Writing to a file 87 | if strcmpi(FileName(end-2:end), 'dat') 88 | fileoutname = FileName; 89 | else 90 | fileoutname = strcat(FileName(1:end-4),'_expMDF.dat'); 91 | end 92 | disp(' ');disp(['Writing data to dat file -- "' fileoutname '"']) ; 93 | fileoutname = strcat(PathName,fileoutname); 94 | expMDF(dataTable, fileoutname); 95 | disp(' '); 96 | disp(['Formatted data written to ',fileoutname]); 97 | disp(repmat(char('-'), 1, 50)); 98 | disp(' '); 99 | end 100 | 101 | %% converts timeseries to structure 102 | function dout = classStruct(din) 103 | dout =struct; 104 | names = fieldnames(din); 105 | for n = 1:length(names) 106 | data_inside = eval(strcat('din.',names{n})); 107 | if isa(data_inside, 'timeseries') 108 | if ~isfield(dout,'time') 109 | dout.time = single(data_inside.Time); 110 | end 111 | varout = strcat('dout.',names{n}); 112 | eval([ varout '= single(data_inside.Data);']); 113 | 114 | elseif isstruct(data_inside) 115 | d = classStruct(data_inside); 116 | name_d = fieldnames(d); 117 | for nd = 1:length(name_d) 118 | varout = strcat('dout.',name_d{nd}); 119 | varval = eval(strcat('d.',name_d{nd})); 120 | eval([ varout '= varval;']); 121 | end 122 | 123 | elseif isa(data_inside, 'Simulink.SimulationData.Dataset') 124 | dset = classSimDataset(data_inside); 125 | d = classStruct(dset); 126 | name_d = fieldnames(d); 127 | for nd = 1:length(name_d) 128 | varout = strcat('dout.',name_d{nd}); 129 | varval = eval(strcat('d.',name_d{nd})); 130 | eval([ varout '= varval;']); 131 | end 132 | 133 | elseif isfloat(data_inside) 134 | if ~isfield(dout,'time') 135 | dout.time = single([0:1:length(data_inside)-1]'); 136 | end 137 | varout = strcat('dout.',names{n}); 138 | eval([ varout '= single(data_inside);']); 139 | end 140 | end 141 | end 142 | 143 | %% converts Simulation Dataset to structure-timeseries 144 | function allSignals = classSimDataset(dataStream) 145 | % todo check if data is fixed step or variable step 146 | % todo check isnan or isinf 147 | % for fixed or variable step, timestamp should be the same 148 | 149 | % 1: extract all the signals from logsout 150 | allSignals = struct; 151 | for var = 1:numElements(dataStream) 152 | dataSignal = dataStream.getElement(var); 153 | % check if signal value type is timeseries (i.e. signals or mux) 154 | if isobject(dataSignal.Values) 155 | allSignals = UpdateSignalsList(allSignals, dataSignal); 156 | 157 | elseif isstruct(dataSignal.Values) % it must be a bus type signal 158 | fnames = fieldnames(dataSignal.Values); 159 | tempSignal = dataSignal; 160 | for i=1:length(fnames) 161 | eval(['tempSignal.Values = dataSignal.Values.' fnames{i} ';']); 162 | tempSignal.Name = [dataSignal.Name '_' tempSignal.Values.Name]; 163 | tempSignal.Values.Name = tempSignal.Name; 164 | allSignals = UpdateSignalsList(allSignals, tempSignal); 165 | end 166 | 167 | else % unknown type 168 | % todo 169 | end 170 | 171 | end 172 | 173 | % 2: get the common timestamp for all the signals 174 | commonTime = []; 175 | signalNames = fieldnames(allSignals); 176 | for i=1:length(signalNames) 177 | tempTS = eval(['allSignals.' signalNames{i}]); 178 | if tempTS.TimeInfo.Length > length(commonTime) 179 | commonTime = tempTS.Time; 180 | end 181 | end 182 | % first element the common timestamp is more than 2s, then set warning 183 | if isempty(commonTime(1) > 2) 184 | warning('No Uniform TimeVector Found: Try logging atleast one signal without the enable/tigger/function subsystem'); 185 | end 186 | 187 | % 3: fix the non-uniform signals 188 | helptext = true; 189 | promptinput = true; 190 | for i=1:length(signalNames) 191 | tempTS = eval(['allSignals.' signalNames{i}]); 192 | if (tempTS.TimeInfo.Length ~= length(commonTime)) 193 | % display one time help text 194 | if (helptext) 195 | helptext = false; 196 | fprintf('\n'); 197 | disp('---------------------------------------------------------------------------'); 198 | fprintf('Some signals are enable/function/trigger subsystem signals and have missing values,\nchoose one of the option below for respective signals - \n'); 199 | fprintf('\t "z" - fill with zeros (default)\n') 200 | fprintf('\t "h" - hold last sample value \n') 201 | fprintf('\t "i" - interpolate in between\n') 202 | fprintf('\t "za" - fill with zeros and use this option for rest of the signals\n') 203 | fprintf('\t "ha" - hold last sample values and use this option for rest of the signals\n') 204 | fprintf('\t "ia" - interpolate in between and use this option for rest of the signals\n') 205 | disp('---------------------------------------------------------------------------'); 206 | fprintf('\n'); 207 | end 208 | 209 | % prompt for fill type, default zero 210 | if (promptinput) 211 | commandwindow; 212 | reply = input([tempTS.Name ' - z/h/i/za/ha/ia [z]:'],'s'); 213 | % any other input default to zero 214 | if (isempty(reply) ||... 215 | isempty(cell2mat(strfind({'z','h','i','za','ha','ia'}, lower(reply))))) 216 | reply = 'z'; 217 | end 218 | % use same option for rest of the signals 219 | if length(reply)>1 220 | promptinput = false; 221 | end 222 | end 223 | 224 | % signal processing 225 | [~, idx] = intersect(commonTime, tempTS.Time, 'stable'); 226 | newData = zeros(size(commonTime)); 227 | if strcmpi(reply(1),'z') 228 | % fill missing values with zeros 229 | newData(idx) = tempTS.Data; 230 | if ~promptinput 231 | fprintf([tempTS.Name ': zero fill\n']); 232 | end 233 | 234 | elseif strcmpi(reply(1),'h') 235 | % hold last samples 236 | newData(idx) = tempTS.Data - [0;tempTS.Data(1:end-1)]; 237 | newData = cumsum(newData); 238 | if ~promptinput 239 | fprintf([tempTS.Name ': hold last value\n']); 240 | end 241 | 242 | elseif strcmpi(reply(1),'i') 243 | % interpolate missing values 244 | if (tempTS.TimeInfo.Length > 2) 245 | newData = interp1(tempTS.Time, tempTS.Data, commonTime, 'linear','extrap'); 246 | if ~promptinput 247 | fprintf([tempTS.Name ': interpolate in between\n']); 248 | end 249 | else 250 | fprintf('\n'); 251 | fprintf(['"' tempTS.Name '" - signal has less than 3 samples to interpolate, defaulting to zero fill\n']); 252 | newData(idx) = tempTS.Data; 253 | end 254 | 255 | end 256 | newTS = timeseries(newData, commonTime, 'name', tempTS.Name); 257 | eval(['allSignals.' tempTS.Name ' = newTS;']); 258 | 259 | end 260 | end 261 | 262 | end 263 | 264 | function allSignals = UpdateSignalsList(allSignals, varSignal) 265 | % check vectors vs signal 266 | if (size(varSignal.Values.Data, 2) == 1) % only signal 267 | % fix time to round off to 5 digits 268 | varSignal.Values.Time = round(varSignal.Values.Time*1e5)/1e5; 269 | eval(['allSignals.' varSignal.Name ' = varSignal.Values;']); 270 | else % vector 271 | tempTS = varSignal.Values; 272 | % fix time to round off to 5 digits 273 | tempTS.Time = round(tempTS.Time*1e5)/1e5; 274 | for i = 1:size(varSignal.Values.Data, 2) 275 | tempTS.Data = varSignal.Values.Data(:,i); 276 | tempTS.Name = [varSignal.Values.Name '_ls_' num2str(i-1) '_rs_']; 277 | eval(['allSignals.' tempTS.Name ' = tempTS;']); 278 | end 279 | end 280 | end 281 | 282 | --------------------------------------------------------------------------------