1 function [] = LogConvert(varargin)
2 % Convert log files into matlab structures
4 % If called with no arguements
this function will present
a
5 %
file chooser. If called with
a string arguement it will
6 % process that
file name.
8 % To process Overo files call with
9 % LogConvert(path_to_file,
true)
11 % Tau Labs (C) 2012-2013
14 %% Define indices and arrays of structures to hold
data
15 % THIS FILE IS AUTOMATICALLY GENERATED.
18 uavoImporterHash= '${TAG_OR_BRANCH}:${HASH8}${DIRTY} ${DATETIME}
';
19 UAVO_HASH = '${UAVOSHA1TXT}
';
21 outputType='mat'; %Default output is a .mat file
26 lastWrongMessageByte=0;
32 multipleInstanceLookup = zeros(0,2);
35 fprintf('\n\n***dRonin log parser***
\n\n');
38 hex2dec('00
'),hex2dec('07
'),hex2dec('0
e'),hex2dec('09
'),hex2dec('1c
'),hex2dec('1b
'),hex2dec('12
'),hex2dec('15
'),hex2dec('38
'),hex2dec('3f
'),hex2dec('36
'),hex2dec('31
'),hex2dec('24
'),hex2dec('23
'),hex2dec('2
a'),hex2dec('2d
'), ...
39 hex2dec('70
'),hex2dec('77
'),hex2dec('7
e'),hex2dec('79
'),hex2dec('6c
'),hex2dec('6b
'),hex2dec('62
'),hex2dec('65
'),hex2dec('48
'),hex2dec('4f
'),hex2dec('46
'),hex2dec('41
'),hex2dec('54
'),hex2dec('53
'),hex2dec('5
a'),hex2dec('5d
'), ...
40 hex2dec('e0
'),hex2dec('e7
'),hex2dec('ee
'),hex2dec('e9
'),hex2dec('fc
'),hex2dec('fb
'),hex2dec('f2
'),hex2dec('f5
'),hex2dec('d8
'),hex2dec('df
'),hex2dec('d6
'),hex2dec('d1
'),hex2dec('c4
'),hex2dec('c3
'),hex2dec('ca
'),hex2dec('cd
'), ...
41 hex2dec('90
'),hex2dec('97
'),hex2dec('9
e'),hex2dec('99
'),hex2dec('8c
'),hex2dec('8b
'),hex2dec('82
'),hex2dec('85
'),hex2dec('a8
'),hex2dec('af
'),hex2dec('a6
'),hex2dec('a1
'),hex2dec('b4
'),hex2dec('b3
'),hex2dec('ba
'),hex2dec('bd
'), ...
42 hex2dec('c7
'),hex2dec('c0
'),hex2dec('c9
'),hex2dec('ce
'),hex2dec('db
'),hex2dec('dc
'),hex2dec('d5
'),hex2dec('d2
'),hex2dec('ff
'),hex2dec('f8
'),hex2dec('f1
'),hex2dec('f6
'),hex2dec('e3
'),hex2dec('e4
'),hex2dec('ed
'),hex2dec('ea
'), ...
43 hex2dec('b7
'),hex2dec('b0
'),hex2dec('b9
'),hex2dec('be
'),hex2dec('ab
'),hex2dec('ac
'),hex2dec('a5
'),hex2dec('a2
'),hex2dec('8f
'),hex2dec('88
'),hex2dec('81
'),hex2dec('86
'),hex2dec('93
'),hex2dec('94
'),hex2dec('9d
'),hex2dec('9
a'), ...
44 hex2dec('27
'),hex2dec('20
'),hex2dec('29
'),hex2dec('2
e'),hex2dec('3b
'),hex2dec('3c
'),hex2dec('35
'),hex2dec('32
'),hex2dec('1f
'),hex2dec('18
'),hex2dec('11
'),hex2dec('16
'),hex2dec('03
'),hex2dec('04
'),hex2dec('0d
'),hex2dec('0
a'), ...
45 hex2dec('57
'),hex2dec('50
'),hex2dec('59
'),hex2dec('5
e'),hex2dec('4b
'),hex2dec('4c
'),hex2dec('45
'),hex2dec('42
'),hex2dec('6f
'),hex2dec('68
'),hex2dec('61
'),hex2dec('66
'),hex2dec('73
'),hex2dec('74
'),hex2dec('7d
'),hex2dec('7
a'), ...
46 hex2dec('89
'),hex2dec('8
e'),hex2dec('87
'),hex2dec('80
'),hex2dec('95
'),hex2dec('92
'),hex2dec('9b
'),hex2dec('9c
'),hex2dec('b1
'),hex2dec('b6
'),hex2dec('bf
'),hex2dec('b8
'),hex2dec('ad
'),hex2dec('aa
'),hex2dec('a3
'),hex2dec('a4
'), ...
47 hex2dec('f9
'),hex2dec('fe
'),hex2dec('f7
'),hex2dec('f0
'),hex2dec('e5
'),hex2dec('e2
'),hex2dec('eb
'),hex2dec('ec
'),hex2dec('c1
'),hex2dec('c6
'),hex2dec('cf
'),hex2dec('c8
'),hex2dec('dd
'),hex2dec('da
'),hex2dec('d3
'),hex2dec('d4
'), ...
48 hex2dec('69
'),hex2dec('6
e'),hex2dec('67
'),hex2dec('60
'),hex2dec('75
'),hex2dec('72
'),hex2dec('7b
'),hex2dec('7c
'),hex2dec('51
'),hex2dec('56
'),hex2dec('5f
'),hex2dec('58
'),hex2dec('4d
'),hex2dec('4
a'),hex2dec('43
'),hex2dec('44
'), ...
49 hex2dec('19
'),hex2dec('1
e'),hex2dec('17
'),hex2dec('10
'),hex2dec('05
'),hex2dec('02
'),hex2dec('0b
'),hex2dec('0c
'),hex2dec('21
'),hex2dec('26
'),hex2dec('2f
'),hex2dec('28
'),hex2dec('3d
'),hex2dec('3
a'),hex2dec('33
'),hex2dec('34
'), ...
50 hex2dec('4
e'),hex2dec('49
'),hex2dec('40
'),hex2dec('47
'),hex2dec('52
'),hex2dec('55
'),hex2dec('5c
'),hex2dec('5b
'),hex2dec('76
'),hex2dec('71
'),hex2dec('78
'),hex2dec('7f
'),hex2dec('6
a'),hex2dec('6d
'),hex2dec('64
'),hex2dec('63
'), ...
51 hex2dec('3
e'),hex2dec('39
'),hex2dec('30
'),hex2dec('37
'),hex2dec('22
'),hex2dec('25
'),hex2dec('2c
'),hex2dec('2b
'),hex2dec('06
'),hex2dec('01
'),hex2dec('08
'),hex2dec('0f
'),hex2dec('1
a'),hex2dec('1d
'),hex2dec('14
'),hex2dec('13
'), ...
52 hex2dec('ae
'),hex2dec('a9
'),hex2dec('a0
'),hex2dec('a7
'),hex2dec('b2
'),hex2dec('b5
'),hex2dec('bc
'),hex2dec('bb
'),hex2dec('96
'),hex2dec('91
'),hex2dec('98
'),hex2dec('9f
'),hex2dec('8
a'),hex2dec('8d
'),hex2dec('84
'),hex2dec('83
'), ...
53 hex2dec('de
'),hex2dec('d9
'),hex2dec('d0
'),hex2dec('d7
'),hex2dec('c2
'),hex2dec('c5
'),hex2dec('cc
'),hex2dec('cb
'),hex2dec('e6
'),hex2dec('e1
'),hex2dec('e8
'),hex2dec('ef
'),hex2dec('fa
'),hex2dec('fd
'),hex2dec('f4
'),hex2dec('f3
') ...
58 if (exist('uigetfile
')) %#ok<EXIST>
59 [FileName, PathName]=uigetfile({'*.drlog;*.tll
','dRonin Log Files (*.drlog, *.tll)
'});
60 logfile=fullfile(PathName, FileName);
63 error('Your technical computing program does
not support
file choosers. Please input the
file name in the argument.
')
67 if nargin>1 && strcmp(class(varargin{2}),'char') == 1
68 outputType=varargin{2};
69 elseif nargin>1 && strcmp(class(varargin{2}),'logical
') == 1
74 if ~strcmpi(outputType,'mat') && ~strcmpi(outputType,'csv')
75 error('Incorrect
file format specified. Second argument must be
''mat'' or
''csv''.
');
83 fgetl(fid); %Read first line
84 gitLogfileHash = fgetl(fid); %Read second line
85 uavoLogfileHash = fgetl(fid); %Read third line
88 if strcmp(gitLogfileHash,uavoImporterHash) == 0
89 warning('Git hashes
do not match. Incorrect
file importer.
'); %#ok<WNTAG>
91 if strcmp(uavoLogfileHash,UAVO_HASH) == 0
92 warning('UAVO hashes
do not match. Incorrect
file importer.
'); %#ok<WNTAG>
98 buffer=fread(fid,Inf,'uchar=>uchar
');
103 correctMsgByte=hex2dec('20
');
104 correctSyncByte=hex2dec('3C
');
105 unknownObjIDList=zeros(1,2);
107 % Parse log file, entry by entry
108 % prebuf = buffer(1:12);
115 instanceIdOffset = -2;
116 timestampWraparound = 2^16;
118 instanceIdOffset = 0;
119 timestampWraparound = 2^32;
122 timestampAccumulator = 0;
125 buffer_len = length(buffer);
127 while bufferIdx < (buffer_len - 20)
128 %% Read message header
129 % get sync field (0x3C, 1 byte)
131 sync = buffer(bufferIdx+12);
133 sync = buffer(bufferIdx);
136 if sync ~= correctSyncByte
137 bufferIdx=bufferIdx+1;
138 if ~overo || sync ~= 255
139 wrongSyncByte = wrongSyncByte + 1;
145 % For GCS logging the format is as follows
146 % 4 bytes timestamp (milliseconds)
148 % UAVTalk packet (always without timestamped packets)
150 % Message type (1 byte)
152 % Object ID (4 bytes)
153 % Instance ID (optional, 2 bytes)
154 % Data (variable length)
157 % Process header, if we are aligned
158 datasizeBufferIdx = bufferIdx; %Just grab the index. We'll
do a typecast later,
if necessary
160 msgType =
buffer(
bufferIdx+13); %
get msg type (quint8 1 byte ) should be 0x20, ignore the rest?
164 % Advance
buffer past header to where
data is (or instance ID)
167 % For Overo logging the format is
168 %
UAVTalk packet (with timestamped packet)
170 % Message type (1 byte, adds 0x80)
172 % Object ID (4 bytes)
173 % Instance ID (optional, 2 bytes)
174 % Timestamp (2 bytes)
175 % Data (variable length)
178 % Process header
for overo
179 datasizeBufferIdx = bufferIdx + 2;
181 msgType =
buffer(bufferIdx+1) - 128;
182 objID = typecast(
buffer(bufferIdx+4:bufferIdx+ 4+4-1),
'uint32');
186 timestamp = double(typecast(
buffer(bufferIdx+8:bufferIdx+10-1),
'uint16'));
188 timestamp = double(typecast(
buffer(bufferIdx+12:bufferIdx+14-1),
'uint16'));
191 % Advance
buffer past header to where
data is. In the
case of
a
192 % multiple instance
object this will be where the timstamp is but
193 % the parsing code will advance by two more.
194 bufferIdx = bufferIdx + 10;
202 timestamp = timestamp + timestampAccumulator;
204 %Check that message type is correct
220 if isempty(unknownObjIDListIdx)
226 datasize = typecast(
buffer(datasizeBufferIdx + 4:datasizeBufferIdx + 12-1),
'uint64');
228 msgBytesLeft = datasize - 1 - 1 - 2 - 4;
229 if msgBytesLeft > 255
232 bufferIdx=bufferIdx+double(msgBytesLeft);
235 % One of the reads failed - indicates EOF
247 str1=[str1 sprintf('\b')]; %#ok<AGROW>
250 str3=sprintf(
'wrongMessageByte instances: % 10d\n\n', wrongMessageByte );
252 str4=sprintf(
'Completed bytes: % 9d of % 9d\n', bufferIdx,
buffer_len);
254 % Arbitrary times two so that it is at least as
long
260 str5=sprintf(
'Est. time remaining, %02dh:%02dm:%02ds \n',
h,
m,
s);
271 % bufferIdx=bufferIdx+12;
276 for i=2:size(unknownObjIDList,1) %Don
't show the first one, as it was simply a dummy placeholder
277 disp(['Unknown
object ID: 0
x' dec2hex(unknownObjIDList(i,1),8) ' appeared
' int2str(unknownObjIDList(i,2)) ' times.
']);
280 %% Clean Up and Save mat file
287 %% Perform typecasting on vectors
291 if strcmpi(outputType,'mat
')
292 [path, name]=fileparts(logfile);
293 matfile = fullfile(path,[name '.mat
']);
294 save(matfile $(SAVEOBJECTSCODE));
299 fprintf('%d records in %0.2f seconds.\n
', buffer_len, etime(clock,startTime));
304 function OPLog2csv(structIn, structName, logfile)
305 %Get each field name from the structure
306 fieldNames = fieldnames(structIn);
308 %Create a text string with the field names
309 headerOut=sprintf('%
s,
',fieldNames{:});
310 headerOut=headerOut(1:end-1); %Trim off last `,` and `\t`
312 %Assign the structure arrays to a matrix.
313 matOut=zeros(max(size(structIn.(fieldNames{1}))), length(fieldNames));
315 if isempty(structIn.(fieldNames{1}));
318 for i=1:length(fieldNames)
319 matOut(:,i)=structIn.(fieldNames{i});
322 % Create filename by replacing opl by csv
323 [path, name] = fileparts(logfile);
324 csvDirName=[name '_csv
'];
325 [dummyA, dummyB]=mkdir(fullfile(path, csvDirName)); %Dummy outputs so the program doens't throw warnings about
"Directory already exists"
335 for i = 1:length(
data)
340 %% This
function was inspired by Bruno Luong'
s 'mcolon'. The name is kept the same as his 'mcolon'
341 %
function, found on Matlab'
s file exchange. The two functions return identical
343 % code would make this function non-cross-platform, so
a Matlab scripted
344 % version is provided here.
345 if size(inStart,1) > 1 || size(
inFinish,1) > 1
346 if size(inStart,2) > 1 || size(inFinish,2) > 1
360 for i=1:length(inStart)
if isempty(structIn.(fieldNames{1}))
Trim off and t Assign the structure arrays to a matrix matOut
Arbitrary times two so that it is at least as long estTimeRemaining
end end Create filename by replacing opl by csv[path, name]
while bufferIdx< (buffer_len-20)%%Read message header%get sync field(0x3C, 1 byte) if~overo sync=buffer(bufferIdx+12);else sync=buffer(bufferIdx);end if sync~=correctSyncByte bufferIdx=bufferIdx+1;if~overo||sync~=255 wrongSyncByte=wrongSyncByte+1;end continue end if~overo%For GCS logging the format is as follows%4 bytes timestamp(milliseconds)%8 bytes data size%UAVTalk packet(always without timestamped packets)%Sync val(0x3c)%Message type(1 byte)%Length(2 bytes)%Object ID(4 bytes)%Instance ID(optional, 2 bytes)%Data(variable length)%Checksum(1 byte)%Process header, if we are aligned datasizeBufferIdx=bufferIdx;%Just grab the index.We'll do a typecast later, if necessary datasizeLength=4;msgType=buffer(bufferIdx+13);%get msg type(quint8 1 byte) should be 0x20, ignore the rest?objID=typecast(buffer(bufferIdx+16:bufferIdx+16+4-1), 'uint32');%get obj id(quint32 4 bytes) timestamp=double(typecast(buffer(bufferIdx:bufferIdx+4-1),'uint32'));%Advance buffer past header to where data is(or instance ID) bufferIdx=bufferIdx+20;else%For Overo logging the format is%UAVTalk packet(with timestamped packet)%Sync val(0x3c)%Message type(1 byte, adds 0x80)%Length(2 bytes)%Object ID(4 bytes)%Instance ID(optional, 2 bytes)%Timestamp(2 bytes)%Data(variable length)%Checksum(1 byte)%Process header for overo datasizeBufferIdx=bufferIdx+2;datasizeLength=2;msgType=buffer(bufferIdx+1)-128;objID=typecast(buffer(bufferIdx+4:bufferIdx+4+4-1), 'uint32');singleInstance=multipleInstanceLookup(multipleInstanceLookup(:, 1)==objID, 2);if singleInstance timestamp=double(typecast(buffer(bufferIdx+8:bufferIdx+10-1),'uint16'));else timestamp=double(typecast(buffer(bufferIdx+12:bufferIdx+14-1),'uint16'));end%Advance buffer past header to where data is.In the case of a%multiple instance object this will be where the timstamp is but%the parsing code will advance by two more.bufferIdx=bufferIdx+10;end if timestamp< lastTimestamp timestampAccumulator=timestampAccumulator+timestampWraparound;end lastTimestamp=timestamp;timestamp=timestamp+timestampAccumulator;%Check that message type is correct if msgType~=correctMsgByte wrongMessageByte=wrongMessageByte+1;continue end if(isempty(objID))%End of file break;end%%Read object try switch objID $(SWITCHCODE) otherwise unknownObjIDListIdx=find(unknownObjIDList(:, 1)==objID, 1, 'first');if isempty(unknownObjIDListIdx) unknownObjIDList=[unknownObjIDList;uint32(objID) 1];%#ok< AGROW > else unknownObjIDList(unknownObjIDListIdx, 2)=unknownObjIDList(unknownObjIDListIdx, 2)+1;end datasize=typecast(buffer(datasizeBufferIdx+4:datasizeBufferIdx+12-1), 'uint64');msgBytesLeft=datasize-1-1-2-4;if msgBytesLeft > 255 msgBytesLeft=0;end bufferIdx=bufferIdx+double(msgBytesLeft);end catch%One of the reads failed-indicates EOF break;end if(wrongSyncByte~=lastWrongSyncByte||wrongMessageByte~=lastWrongMessageByte)||...bufferIdx-last_print > Every
Dummy outputs so the program doens t throw warnings about Directory already exists csvfile
Create a text string with the field names headerOut
end Check if at end of file If not
else(EXPORTCSVCODE) end fprintf('%d records in%0.2f seconds.\n'
Write to csv dlmwrite(csvfile, headerOut, '')
end function found on Matlab s file exchange The two functions return identical although his is much faster Unfortunately
Prune vectors(CLEANUPCODE)%%Perform typecasting on vectors $(ALLOCATIONCODE)%%Save data to file if strcmpi(outputType
end(INSTANTIATIONCODE) fid
end function found on Matlab s file exchange The two functions return identical results
else error('Your technical computing program does not support file choosers.Please input the file name in the argument. ') end elseif nargin >0 logfile
'Inputs must be vectors, i.e just one column wide.' inStart()
fprintf('\n\n ***dRonin log parser ***\n\n')