Master Thesis Code
by Simon Moser
Loading...
Searching...
No Matches
jumpReadData.m
Go to the documentation of this file.
1% =========================================================================== %
2%> @brief reads binary files of JUMP Sensors
3%>
4%> this function reads the binary file provided by the JUMP Sensor and returns
5%> a struct containing the data.
6%>
7%> @param filePath - full path to the binary file
8%> @param options.time_correction - boolean to enable time correction (default: true)
9%> @param options.plot_time_correction - boolean to plot the time correction (default: false)
10%> @param options.time_jump_correction - boolean to correct time jumps (default: true)
11%> @param options.plot_time_jump_correction - boolean to plot the time jump correction (default: false)
12%> @param options.header_only - boolean to only read the header of the file (default: false)
13%>
14%> @retval data - struct containing the data. the fields are defined in
15%> @ref jumpCheckData
16%>
17%> @note
18%> - This implementation is inspired by the implementation of Tobias Voegeli
19%> (ETHZ) from 2016.
20%> - This function also undoes timing errors made in the firmware. See @ref
21%> jumpCorrectData for more information.
22%>
23%> @code{.m}
24%> % example usage:
25%> data = jumpReadData("jump_1.BIN");
26%> @endcode
27%>
28%> @file jumpReadData.m
29%>
30%> @copyright see the file @ref LICENSE in the root directory of the repository
31% =========================================================================== %
32
33function data = jumpReadData(filePath, options)
34
35arguments
36 filePath {mustBeFile}
37 options.time_correction (1,1) logical = true
38 options.plot_time_correction (1,1) logical = false
39 options.time_jump_correction (1,1) logical = true
40 options.plot_time_jump_correction (1,1) logical = false
41 options.header_only (1,1) logical = false
42end
43
44FILE_HEADER = 512;
45SAMPLE_SIZE = 59;
46
47fileID = fopen(filePath);
48if options.header_only
49 data = uint8(fread(fileID, FILE_HEADER, 'uint8'));
50else
51 data = uint8(fread(fileID, inf, 'uint8'));
52end
53fclose(fileID);
54
55%% check file consistency
56% check for correct length (and correct if necessary)
57len = (numel(data)-FILE_HEADER)/SAMPLE_SIZE;
58if(rem(len,1)~=0)
59 len = floor(len);
60 data(SAMPLE_SIZE*len+1+FILE_HEADER:end) = []; % erase incomplete last block
61end
62
63% check if newline is at correct location
64cr = data(FILE_HEADER+58:SAMPLE_SIZE:end);
65lf = data(FILE_HEADER+59:SAMPLE_SIZE:end);
66
67err = [find(lf~=10) find(cr~=13)];
68if(~isempty(err))
69 nerr = numel(unique(err));
70 warning(['Error parsing file. Inconsistent block format. ' num2str(nerr) ' faulty block(s) found.'])
71end
72
73%% parse data block
74% file header
75ID = sprintf(strcat(char(data(22:33))));
76UID = num2str(swapbytes(typecast(data(50:57),'uint64')));
77name = sprintf(strcat(char(data(58:73))));
78sw_version = strcat(num2str(typecast(data(74:77),'uint32')), '(', data(78),')');
79hw_version = strcat(num2str(typecast(data(80:83),'uint32')), '(', data(79),')');
80acc_range = double(data(84));
81gyro_range = double(uint32(data(85)) * 10);
82mpu_cal = double(data(86));
83act_thrs = double(data(87));
84inact_dur = double(data(88));
85mpu_rate = double(data(89));
86adxl_rate = double(data(90));
87pres_rate = double(data(91));
88magn_en = double(data(92));
89sync = double(data(93));
90ble_address = dec2hex(uint64(typecast(data(96:99),'uint32'))*2^16 + uint64(data(95))*2^8 + uint64(data(94)));
91ble_address = dec2hex(swapbytes(uint64(hex2dec(ble_address))));
92ble_address = ble_address(1:end-4);
93
94if ~options.header_only
95
96
97 % time
98 sec = double(typecast( reshape([data(FILE_HEADER+1:SAMPLE_SIZE:end), data(FILE_HEADER+2:SAMPLE_SIZE:end), data(FILE_HEADER+3:SAMPLE_SIZE:end), data(FILE_HEADER+4:SAMPLE_SIZE:end)]',1,[]), 'uint32'));
99 subsec = double(typecast( reshape([data(FILE_HEADER+5:SAMPLE_SIZE:end), data(FILE_HEADER+6:SAMPLE_SIZE:end)]',1,[]), 'uint16'));
100 t = (sec+subsec/2^15)';
101 t = datetime(1970,1,1,0,0,0) + seconds(t);
102
103 % acceleration (from Ivensense MPU-9250)
104 acc = zeros(len,3);
105 acc(:,1) = typecast( reshape([data((FILE_HEADER+7):SAMPLE_SIZE:end), data((FILE_HEADER+8):SAMPLE_SIZE:end)]',1,[]), 'int16');
106 acc(:,2) = typecast( reshape([data((FILE_HEADER+9):SAMPLE_SIZE:end), data((FILE_HEADER+10):SAMPLE_SIZE:end)]',1,[]), 'int16');
107 acc(:,3) = typecast( reshape([data((FILE_HEADER+11):SAMPLE_SIZE:end), data((FILE_HEADER+12):SAMPLE_SIZE:end)]',1,[]), 'int16');
108 acc = double(acc) / double(int32(0x10000) / (2 * int32(acc_range))); % rescale to unit 'g'
109 acc = acc * 9.80665; % standard gravity (BIPM, 1901)
110
111 % angular rate (from Ivensense MPU-9250)
112 gyro = zeros(len,3);
113 gyro(:,1) = typecast( reshape([data(FILE_HEADER+13:SAMPLE_SIZE:end), data(FILE_HEADER+14:SAMPLE_SIZE:end)]',1,[]), 'int16');
114 gyro(:,2) = typecast( reshape([data(FILE_HEADER+15:SAMPLE_SIZE:end), data(FILE_HEADER+16:SAMPLE_SIZE:end)]',1,[]), 'int16');
115 gyro(:,3) = typecast( reshape([data(FILE_HEADER+17:SAMPLE_SIZE:end), data(FILE_HEADER+18:SAMPLE_SIZE:end)]',1,[]), 'int16');
116 gyro = double(gyro) / (double(int32(0x10000)) / double(2 * int32(gyro_range))); % rescale to unit '°/s'
117
118 % high G acceleration (from Analog Devices ADXL375)
119 accHiG = zeros(len,3);
120 accHiG(:,1) = double(typecast( reshape([data(FILE_HEADER+35:SAMPLE_SIZE:end), data(FILE_HEADER+36:SAMPLE_SIZE:end)]',1,[]), 'int16'));
121 accHiG(:,2) = double(typecast( reshape([data(FILE_HEADER+37:SAMPLE_SIZE:end), data(FILE_HEADER+38:SAMPLE_SIZE:end)]',1,[]), 'int16'));
122 accHiG(:,3) = double(typecast( reshape([data(FILE_HEADER+39:SAMPLE_SIZE:end), data(FILE_HEADER+40:SAMPLE_SIZE:end)]',1,[]), 'int16'));
123 accHiG = accHiG / 20.5; % rescale to unit 'g'
124 accHiG = accHiG * 9.80665; % standard gravity (BIPM, 1901)
125
126 % Magnetometer (from Ivensense MPU-9250)
127 magn = zeros(len,3);
128 magn(:,1) = double(typecast( reshape([data(FILE_HEADER+41:SAMPLE_SIZE:end), data(FILE_HEADER+42:SAMPLE_SIZE:end)]',1,[]), 'int16'));
129 magn(:,2) = double(typecast( reshape([data(FILE_HEADER+43:SAMPLE_SIZE:end), data(FILE_HEADER+44:SAMPLE_SIZE:end)]',1,[]), 'int16'));
130 magn(:,3) = double(typecast( reshape([data(FILE_HEADER+45:SAMPLE_SIZE:end), data(FILE_HEADER+46:SAMPLE_SIZE:end)]',1,[]), 'int16'));
131 magn = magn / 0.6; % rescale to unit 'uT'
132
133 % pressure (from TE Connectivity MS5611-01BA03)
134 pressure = double(typecast( reshape([data(FILE_HEADER+47:SAMPLE_SIZE:end), data(FILE_HEADER+48:SAMPLE_SIZE:end), data(FILE_HEADER+49:SAMPLE_SIZE:end), data(FILE_HEADER+50:SAMPLE_SIZE:end)]',1,[]), 'int32'))';
135 pressure = pressure / 100; % rescale to unit 'mbar'
136
137 % temp (from TE Connectivity MS5611-01BA03)
138 temp = double(typecast( reshape([data(FILE_HEADER+51:SAMPLE_SIZE:end), data(FILE_HEADER+52:SAMPLE_SIZE:end), data(FILE_HEADER+53:SAMPLE_SIZE:end), data(FILE_HEADER+54:SAMPLE_SIZE:end)]',1,[]), 'int32'))';
139 temp = temp / 100; % rescale to unit '°C'
140
141end
142
143%% create data struct
144data = struct;
145data.ID = ID;
146data.UID = UID;
147data.name = name;
148data.sw_version = sw_version;
149data.hw_version = hw_version;
150data.acc_range = acc_range;
151data.gyro_range = gyro_range;
152data.mpu_cal = mpu_cal;
153data.act_thrs = act_thrs;
154data.inact_dur = inact_dur;
155data.mpu_rate = mpu_rate;
156data.adxl_rate = adxl_rate;
157data.pres_rate = pres_rate;
158data.magn_en = magn_en;
159data.sync = sync;
160data.ble_address = ble_address;
161
162if options.header_only
163 return
164end
165
166data.tt = timetable(t,acc,gyro,accHiG,magn,pressure,temp);
167data.tt.Properties.VariableNames = {'acc','gyr','hig','mag','prs','tmp'};
168data.tt.Properties.VariableUnits = {'m/s^2','°/s','m/s^2','uT','mbar','°C'};
169data.tt.t.Format = "dd.MM.uuuu HH:mm:ss.SSS";
170data.t_start = data.tt.t(1);
171
172% correct time jumps in the data
173% there are binary files that have jumps in the full second timestamps. The
174% subsecond timestamps, however, are correct. therefore this section corrects
175% the full second timestamps to be continuous.
176
177% time derivative
178t_diff = diff(data.tt.t);
179
180% check for jumps (a jump is defined as a difference of more than 1 second)
181if any(abs(t_diff) > seconds(1))
182 assert(options.time_jump_correction, 'Time jumps detected. Cannot correct. Set option time_jump_correction to true.')
183
184 t_diff_old = t_diff;
185
186 % split the difference into full seconds and subseconds
187 t_diff_sec = round(seconds(t_diff));
188 t_diff_subsec = seconds(t_diff) - t_diff_sec;
189
190 % check iff there are jumps in the subseconds (less that 3 ms or more than 7 ms)
191 assert(all(abs(t_diff_subsec) > 3e-3) && all(abs(t_diff_subsec) < 7e-3), 'Subsecond jumps detected. Cannot correct.')
192
193 % check if the seconds difference sums up to zero
194 assert(sum(t_diff_sec) == 0, 'Seconds difference does not sum up to zero. Cannot correct.')
195
196 % create the correction time vector
197 t_corr = -cumsum(t_diff_sec);
198
199 % correct the time
200 data.tt.t(2:end) = data.tt.t(2:end) + seconds(t_corr);
201
202 % check if the correction was successful
203 assert(all(abs(diff(data.tt.t)) < seconds(1)), 'Time correction failed.')
204
205 % plot the correction
206 if options.plot_time_jump_correction
207 figure
208 yyaxis left
209 plot(diff(data.tt.t), '.', DisplayName="Corrected time difference [s]")
210 ylabel('Corrected time difference [s]')
211 yyaxis right
212 stem(t_diff_old, '.', MarkerSize=15, DisplayName="Original time difference [s]")
213 ylabel('Time difference [s]')
214 grid on;
215 title('Time jump correction')
216 legend(Location="best")
217 end
218
219end
220
221% bring the timetable to regular sampling rate
222if options.time_correction
223 % correct the time
224 data.tt = jumpCorrectData(data.tt, plot=options.plot_time_correction);
225else
226 % resample the data
227 data.tt = retime(data.tt, 'regular', 'linear', 'TimeStep', seconds(1/data.mpu_rate));
228
229 if options.plot_time_correction
230 warning('Time correction is disabled. Cannot plot time correction.')
231 end
232end
233
234% check output
235[check, ~, data] = jumpCheckData(data);
236
237if ~check
238 error('Data check failed. Check the data struct.')
239end
240
241end
function jumpCheckData(in data)
function jumpCorrectData(in tt, in options)
function jumpReadData(in filePath, in options)