cgv
Loading...
Searching...
No Matches
vr_log.cxx
1#include "vr_log.h"
2
3#include <sstream>
4
5/* Logfile lines
6<pose> : 12 floats representing a 4*3 matrix (column major)
7<button-mask>: 32 bit integer
8<axis-state>: vr::max_nr_controller_axes floats for the axes of the controller
9<vibration>: 2 floats for the vibration intensity
10<timestamp>: time in ms
11
12<timestamp>,({C <controller_id> [P <pose>] [B <button-mask>] [A <axes-state>] [V <vibration>]},)*[H <pose>]
13*/
14
15//token struct for the parser
16struct token {
17 enum {
18 COMPOUND, //<name> <...>
19 VALUE,// <value>
20 NAME
21 } type;
22 std::string text;
23
24};
25
26template <size_t size, typename T>
27bool array_contains(const T* container, const T& element) {
28 int i = 0;
29 while (i < size) {
30 if (container[i] == element) {
31 return true;
32 }
33 ++i;
34 }
35 return false;
36}
37
38
39template <typename C>
40void tokenize(std::string& line, C& buffer) {
41 std::istringstream l(line);
42 std::string text;
43 while (std::getline(l, text, ',')) {
44 if (text.size()>0 && text.c_str()[0] == '{') { //compound
45 token t;
46 t.type = token::COMPOUND;
47 size_t end = text.find_first_of("}");
48 if (end != std::string::npos) {
49 t.text = text.substr(1, end-1);
50 buffer.push_back(t);
51 }
52 else {
53 throw std::string("parsing error: missing \"}\"");
54 }
55 }
56 else if (text.size() > 0 && text.find_first_of("abcdefghijklmnopqrstuvwxyz") != std::string::npos) {
57 token t;
58 t.type = token::NAME;
59 t.text = text;
60 buffer.push_back(t);
61 }
62 else if (text.size() > 0 && text.find_first_of("0123456789") != std::string::npos) {
63 token t;
64 t.type = token::VALUE;
65 t.text = text;
66 buffer.push_back(t);
67 }
68 }
69}
70
71const std::string filter_to_string(vr::vr_log::Filter f){
72 switch (f) {
73 case vr::vr_log::F_AXES:
74 return "AXES";
75 case vr::vr_log::F_BUTTON:
76 return "BUTTON";
77 case vr::vr_log::F_HMD:
78 return "HMD";
79 case vr::vr_log::F_POSE:
80 return "POSE";
81 case vr::vr_log::F_VIBRATION:
82 return "VIBRATION";
83 default:
84 return "UNKNOWN_FILTER";
85 }
86}
87
88const std::unordered_map<std::string, vr::vr_log::Filter> filter_map = {
89 {"AXES", vr::vr_log::F_AXES},
90 {"BUTTON", vr::vr_log::F_BUTTON},
91 {"HMD", vr::vr_log::F_HMD },
92 {"POSE", vr::vr_log::F_POSE},
93 {"VIBRATION", vr::vr_log::F_VIBRATION},
94 {"UNKNOWN_FILTER", vr::vr_log::F_NONE}
95};
96
97
98const vr::vr_log::Filter filter_from_string(const std::string& f) {
99 const auto it = filter_map.find(f);
100 if (it != filter_map.cend()) {
101 return (*it).second;
102 }
103 return vr::vr_log::F_NONE;
104}
105
106
108{
109 log_storage_mode = SM_NONE;
110 if (log_stream) {
111 log_stream = nullptr;
112 }
113}
114
116{
117 if (!setting_locked)
118 log_storage_mode = log_storage_mode | SM_IN_MEMORY;
119}
120
121void vr::vr_log::enable_ostream_log(const std::shared_ptr<std::ostream>& stream)
122{
123 if (!setting_locked)
124 log_stream = stream;
125 log_stream->precision(std::numeric_limits<double>::max_digits10);
126 log_storage_mode = log_storage_mode | SM_OSTREAM;
127}
128
129vr::vr_log::vr_log(std::istringstream& is) {
130 load_state(is);
131}
132
133void vr::vr_log::log_vr_state(const vr::vr_kit_state& state, const int mode, const int filter, const double time,std::ostream* log_stream)
134{
135 if (!setting_locked)
136 return;
137
138 //time stamp
139 if (mode & SM_IN_MEMORY) {
140 this->time_stamp.push_back(time);
141 }
142 if (mode & SM_OSTREAM) {
143 *(log_stream) << time;
144 }
145 if (mode != SM_NONE) {
146 ++nr_vr_states;
147 }
148
149 //controller state
150 for (int ci = 0; ci < max_nr_controllers; ++ci) {
151 controller_status[ci].push_back(state.controller[ci].status);
152 if (mode & SM_IN_MEMORY) {
153 if (filter & F_VIBRATION) {
154 cgv::vec2 vibration = cgv::vec2(state.controller[ci].vibration[0], state.controller[ci].vibration[1]);
155 this->controller_vibration[ci].push_back(vibration);
156 }
157 if (filter & F_AXES) {
159 for (int j = 0; j < max_nr_controller_axes; ++j) {
160 axes(j) = state.controller[ci].axes[j];
161 }
162 this->controller_axes[ci].push_back(axes);
163 }
164 if (filter & F_POSE) {
165 cgv::mat3x4 pose = cgv::mat3x4(3, 4, state.controller[ci].pose);
166 this->controller_pose[ci].push_back(pose);
167 }
168 if (filter & F_BUTTON) {
169 this->controller_button_flags[ci].push_back(state.controller[ci].button_flags);
170 }
171 }
172 if (mode & SM_OSTREAM) {
173 //<timestamp>, ({C <controller_id> [P <pose>] [B <button - mask>] [A <axes - state>] [V <vibration>] }, )* [H <pose>]
174 //C{<controller_id> [P <pose>] [B <button - mask>] [A <axes - state>] [V <vibration>]}
176 *(log_stream) << ",{C " << ci;
177 if (filter & F_POSE) {
178 *(log_stream) << " P";
179 for (int j = 0; j < 12; ++j)
180 *(log_stream) << ' ' << state.controller[ci].pose[j];
181 }
182 if (filter & F_BUTTON) {
183 *(log_stream) << " B " << state.controller[ci].button_flags;
184 }
185 if (filter & F_AXES) {
186 *(log_stream) << " A";
187 for (int j = 0; j < max_nr_controller_axes; ++j)
188 *(log_stream) << ' ' << state.controller[ci].axes[j];
189 }
190 if (filter & F_VIBRATION) {
191 *(log_stream) << " V";
192 for (int j = 0; j < 2; ++j)
193 *(log_stream) << ' ' << state.controller[ci].vibration[j];
194 }
195 *(log_stream) << "}";
196 }
197 }
198 }
199
200 //hmd state
201 if (filter & F_HMD) {
202 cgv::mat3x4 pose = cgv::mat3x4(3, 4, state.hmd.pose);
203 if (mode & SM_IN_MEMORY) {
204 hmd_pose.push_back(pose);
205 hmd_status.push_back(state.hmd.status);
206 }
207 if ((mode & SM_OSTREAM) && log_stream) {
208 if (state.hmd.status == vr::VRStatus::VRS_TRACKED) {
209 *(log_stream) << ",{H ";
210 for (int j = 0; j < 12; ++j)
211 *(log_stream) << ' ' << state.hmd.pose[j];
212 *(log_stream) << '}';
213 }
214 }
215 }
216 //end line
217 if ((mode & SM_OSTREAM) && log_stream) {
218 *(log_stream) << '\n';
219 }
220}
221
222template <typename T,unsigned SIZE>
223void parse_array(std::istringstream& line,T* storage) {
224 for (int i = 0; i < SIZE; ++i) {
225 line >> storage[i];
226 }
227}
228
229int parse_filter_string(std::istringstream& line) {
230 int filters = 0;
231 while (!line.eof()) {
232 std::string filter;
233 line >> filter;
234 filters |= filter_from_string(filter);
235 }
236 return filters;
237}
238
239//expects controller state string
240unsigned parse_controller_state(std::istringstream& line, vr::vr_controller_state& state) {
241 unsigned filter = 0;
242 std::string cinfo_type;
243
244 while (!line.eof()) {
245 line >> cinfo_type;
246 if (cinfo_type == "P") {
247 filter |= vr::vr_log::F_POSE;
248 parse_array<float, 12>(line, state.pose);
249 }
250 else if (cinfo_type == "A") {
251 filter |= vr::vr_log::F_AXES;
252 parse_array<float, vr::max_nr_controller_axes>(line, state.axes);
253 }
254 else if (cinfo_type == "B") {
255 filter |= vr::vr_log::F_BUTTON;
256 line >> state.button_flags;
257 }
258 else if (cinfo_type == "V") {
259 filter |= vr::vr_log::F_VIBRATION;
260 parse_array<float, 2>(line, state.vibration);
261 }
262 }
263 return filter;
264}
265
266//expects up to 5 COMPOUND tokens, returns active filters found
267template <typename iterator>
268unsigned parse_vr_kit_state(iterator it, iterator last, vr::vr_kit_state& state,double& time) {
269 unsigned filter = 0;
270
271 while (it != last)
272 {
273 if (it->type == token::COMPOUND) {
274 std::string type;
275 std::istringstream line(it->text);
276 line >> type;
277 if (type == "C") { //controller info compund
278 int cid = -1; //controller id
279 line >> cid;
280 if (cid >= 0 && cid < vr::max_nr_controllers)
281 filter |= parse_controller_state(line, state.controller[cid]);
282 else
283 throw std::string("invalid controller id");
284 }
285 else if (type == "H") { //parse hmd info
286 filter |= vr::vr_log::F_HMD;
287 parse_array<float, 12>(line, state.hmd.pose);
288 }
289 }
290 else {
291 throw std::string("unexpected token");
292 //unexpected token
293 }
294 ++it;
295 }
296 return filter;
297}
298
299
301{
302 setting_locked = true;
303 //write header
304 if (log_storage_mode & SM_OSTREAM) {
305 *(log_stream) << "filters,{";
306 int fil = 1;
307 bool first = true;
308 while (fil < F_ALL) {
309 if (fil & filters) {
310 if (!first) {
311 *(log_stream) << " ";
312 } else {
313 first = false;
314 }
315 *(log_stream) << filter_to_string(static_cast<vr::vr_log::Filter>(fil));
316 }
317 fil = fil << 1;
318 }
319 *(log_stream) << "}\n";
320 }
321}
322
323bool vr::vr_log::load_state(std::istringstream& is) {
324 //log lines look like this:
325 //<timestamp>, ({ C <controller_id>[P <pose>][B <button - mask>][A <axes - state>][V <vibration>] }, )* [H <pose>]
326 if (setting_locked)
327 return false;
328 log_storage_mode = SM_IN_MEMORY;
329 set_filter(F_ALL);
330 lock_settings();
331 //write log
332 std::vector<token> tokens;
333 bool found_filters = false;
334
335 try {
336 while (!is.eof()) {
337 std::string line;
338 std::getline(is, line);
339 tokens.clear();
340 tokenize(line, tokens);
341 double time = -1;
342 if (tokens.size()) {
343 if (tokens[0].type == token::VALUE) {
344 if (!found_filters) {
345 return false;
346 }
347 double time = std::stod(tokens[0].text);
348 vr::vr_kit_state state;
349 parse_vr_kit_state(tokens.cbegin() + 1, tokens.cend(), state, time);
350 log_vr_state(state, time);
351 }
352 else if (tokens[0].type == token::NAME) {
353 if (tokens.size() >= 2 && tokens[0].text == "filters" && tokens[1].type == token::COMPOUND) {
354 found_filters = true;
355 auto text = std::istringstream(tokens[1].text);
356 filters = parse_filter_string(text);
357 }
358 }
359 else {
360 throw std::string("parsing error expected time got " + tokens[0].text);
361 }
362 }
363 }
364 }
365 catch (std::string err) {
366 return false;
367 }
368
369 disable_log();
370 return true;
371}
void enable_in_memory_log()
enable in memory log
Definition vr_log.cxx:115
void lock_settings()
prevent changes to settings and enables log_vr_state methods
Definition vr_log.cxx:300
void disable_log()
disable logging
Definition vr_log.cxx:107
void enable_ostream_log(const std::shared_ptr< std::ostream > &stream)
enable writing to ostream.
Definition vr_log.cxx:121
void log_vr_state(const vr::vr_kit_state &state, const int mode, const int filter, const double time, std::ostream *log_stream)
record state
Definition vr_log.cxx:133
bool load_state(std::istringstream &is)
read log from stream
Definition vr_log.cxx:323
cgv::math::fvec< float, 2 > vec2
declare type of 2d single precision floating point vectors
Definition fvec.h:667
cgv::math::fmat< float, 3, 4 > mat3x4
declare type of 3x4 matrices which are often used to store a pose
Definition fmat.h:362
const unsigned max_nr_controller_axes
maximum number of axes per controller
Definition vr_state.h:23
@ VRS_TRACKED
trackable is connected and tracked
Definition vr_state.h:88
const unsigned max_nr_controllers
maximum number of attachable controller and tracker devices
Definition vr_state.h:19
Extends the trackable state by information on the buttons, input axes and vibration strengths.
Definition vr_state.h:118
float axes[max_nr_controller_axes]
up to vr::max_nr_controller_axes axis values in the range [-1,1] or [0,1] (VIVE: 0|1....
Definition vr_state.h:124
float vibration[2]
strength of the vibration motors
Definition vr_state.h:126
unsigned button_flags
combination of flags in VRButtonStateFlags combined with the OR operation
Definition vr_state.h:122
structure that stores all information describing the state of a VR kit
Definition vr_state.h:139
vr_controller_state controller[max_nr_controllers]
status, pose, button, axes, and vibration information of up to vr::max_nr_controllers controller and ...
Definition vr_state.h:143
vr_trackable_state hmd
status and pose of hmd
Definition vr_state.h:141
VRStatus status
whether trackable is currently tracked, only in case of true, the pose member contains useful informa...
Definition vr_state.h:96
float pose[12]
pose as 3x4 matrix in column major format, where each column is a vector in world coordinates
Definition vr_state.h:104