-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcom_motionreality_OSVR_SharedIMU.cpp
More file actions
executable file
·310 lines (257 loc) · 10.4 KB
/
com_motionreality_OSVR_SharedIMU.cpp
File metadata and controls
executable file
·310 lines (257 loc) · 10.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
#include <osvr/PluginKit/PluginKit.h>
#include <osvr/PluginKit/AnalogInterfaceC.h>
#include <osvr/PluginKit/TrackerInterfaceC.h>
#include <Eigen/Core>
#include <Eigen/Geometry>
// Generated JSON header file
#include "com_motionreality_OSVR_SharedIMU_json.h"
#include <iostream>
#include <hidapi.h>
//#pragma optimize("",off)
// Anonymous namespace to avoid symbol collision
namespace {
class OSVR_SharedIMU
{
public:
OSVR_SharedIMU(OSVR_PluginRegContext ctx)
: m_lastStatus(EnStatus_CLOSED)
, m_timeoutCount(0)
, m_pHidDevice(nullptr)
{
if (hid_init())
{
throw std::runtime_error("Failed to initialize HID library");
}
/// Create the initialization options
OSVR_DeviceInitOptions opts = osvrDeviceCreateInitOptions(ctx);
osvrDeviceTrackerConfigure(opts, &m_tracker);
osvrDeviceAnalogConfigure(opts, &m_analog, 2);
/// Create the device token with the options
m_devToken.initSync(ctx, "OSVR_SharedIMU", opts);
/// Send JSON descriptor
m_devToken.sendJsonDescriptor(com_motionreality_OSVR_SharedIMU);
/// Register update callback
m_devToken.registerUpdateCallback(this);
memset(&m_lastDeviceTime, 0, sizeof(m_lastDeviceTime));
}
~OSVR_SharedIMU()
{
CloseDevice();
hid_exit();
}
OSVR_ReturnCode update()
{
OpenDevice();
ProcessMessages();
return OSVR_RETURN_SUCCESS;
}
private:
osvr::pluginkit::DeviceToken m_devToken;
OSVR_TrackerDeviceInterface m_tracker;
OSVR_AnalogDeviceInterface m_analog;
enum EnStatus
{
EnStatus_CLOSED,
EnStatus_FAILED,
EnStatus_OPENED,
} m_lastStatus;
size_t m_timeoutCount;
hid_device * m_pHidDevice;
OSVR_TimeValue m_lastDeviceTime;
unsigned short m_sLastValues[4];
bool OpenDevice()
{
if (m_pHidDevice)
return true;
OSVR_TimeValue tvNow;
osvrTimeValueGetNow(&tvNow);
if (osvrTimeValueDurationSeconds(&tvNow, &m_lastDeviceTime) < 1)
{
return false;
}
std::cerr << "<SharedIMU> Attempting to open HID" << std::endl;
m_lastDeviceTime = tvNow;
memset(&m_sLastValues[0], 0, sizeof(m_sLastValues));
// Open the device using the VID, PID,
// and optionally the Serial number.
struct VenDev
{
unsigned short vendor;
unsigned short device;
} static const s_devices[] = {
{ 0x03EB, 0x2421 },
{ 0x1532, 0x0b00 },
};
hid_device * handle = 0;
for (size_t i = 0; i < sizeof(s_devices) / sizeof(VenDev); ++i)
{
auto const & id = s_devices[i];
// @note We are relying on the modified version of HIDAPI checked in with this
// plug-in that always opens the device in shared mode.
m_pHidDevice = hid_open(id.vendor, id.device, NULL);
if (m_pHidDevice)
{
char buffer[128];
sprintf_s(buffer, "<SharedIMU> Opened OSVR HDK IMU device (VID%04X/PID%04X)", id.vendor, id.device);
std::cerr << buffer << std::endl;
break;
}
}
if (!m_pHidDevice)
{
return false;
}
// Dump all existing data to ensure that we are timestamping fresh data
int ret = 0;
unsigned char buf[256];
while ((ret = hid_read_timeout(m_pHidDevice, buf, sizeof(buf), 0)) > 0)
{
/*No op*/
}
if (ret < 0) // Error while purging data
{
std::cerr << "<SharedIMU> Error reading HID device: " << ret << std::endl;
CloseDevice();
return false;
}
m_timeoutCount = 0;
return true;
}
void CloseDevice()
{
if (m_pHidDevice)
{
hid_close(m_pHidDevice);
m_pHidDevice = nullptr;
osvrTimeValueGetNow(&m_lastDeviceTime);
}
}
void ProcessMessages()
{
if (!m_pHidDevice)
return;
// Initial timeout is high to detect drop out.
// This changes to 0 (no timeout) after a message have been received
// during this update cycle.
int timeout_millis = 100;
struct MsgBuffer
{
OSVR_TimeValue timestamp;
uint8_t data[16];
} msgBuf;
for (size_t i = 0; i < 10; ++i)
{
int nBytesRead = hid_read_timeout(m_pHidDevice, &msgBuf.data[0], sizeof(msgBuf.data), timeout_millis);
if (nBytesRead < 0)
{
std::cerr << "<SharedIMU> Error reading HID device" << std::endl;
m_lastStatus = EnStatus_FAILED;
CloseDevice();
return;
}
if (nBytesRead == 0)
{
if (timeout_millis > 0)
{
if (m_timeoutCount == 0)
{
std::cerr << "<SharedIMU> Timeout reading HID device" << std::endl;
}
++m_timeoutCount;
}
return;
}
// Once we've received a message this update, we just want to check
// quickly if there are any other messages pending
timeout_millis = 0;
m_timeoutCount = 0;
if (nBytesRead < sizeof(msgBuf.data))
{
std::cerr << "<SharedIMU> Runt message @ len = " << nBytesRead << std::endl;
return;
}
unsigned int ver = (msgBuf.data[0] & 0x0F);
unsigned int seq = msgBuf.data[1];
auto const * pShorts = (signed __int16 const *)&msgBuf.data[2];
bool const bChanged = (pShorts[0] != m_sLastValues[0]) ||
(pShorts[1] != m_sLastValues[1]) ||
(pShorts[2] != m_sLastValues[2]) ||
(pShorts[3] != m_sLastValues[3]);
if (!bChanged)
return;
osvrTimeValueGetNow(&msgBuf.timestamp);
for (size_t i = 0; i < 4; ++i)
m_sLastValues[i] = pShorts[i];
double fValues[7];
double const SCALE = 1.f / (1 << 14);
for (size_t i = 0; i < 7; ++i)
{
fValues[i] = pShorts[i] * SCALE;
}
OSVR_OrientationState q;
osvrQuatSetX(&q, fValues[0]);
osvrQuatSetY(&q, fValues[1]);
osvrQuatSetZ(&q, fValues[2]);
osvrQuatSetW(&q, fValues[3]);
osvrDeviceTrackerSendOrientationTimestamped(m_devToken, m_tracker, &q, 0, &msgBuf.timestamp);
if (ver >= 2)
{
auto const dt = 1. / 50.;
auto const rotVec = Eigen::Map<Eigen::Vector3d>(&fValues[4]);
auto const magnitude = rotVec.norm(); // radians per second
auto const rotAxis = (magnitude > 0.0001) ? rotVec / magnitude : Eigen::Vector3d(1, 0, 0);
auto const deltaAngle = magnitude * dt; // radians per dt
auto const qDelta = Eigen::Quaterniond(Eigen::AngleAxisd(deltaAngle, rotAxis));
auto const qBase = Eigen::Map<Eigen::Quaterniond>(&fValues[0]);
auto const qIncRot = (qBase * qDelta * qBase.inverse()).normalized();
OSVR_AngularVelocityState dq;
dq.dt = dt;
osvrQuatSetX(&dq.incrementalRotation, qIncRot.x());
osvrQuatSetY(&dq.incrementalRotation, qIncRot.y());
osvrQuatSetZ(&dq.incrementalRotation, qIncRot.z());
osvrQuatSetW(&dq.incrementalRotation, qIncRot.w());
osvrDeviceTrackerSendAngularVelocityTimestamped(m_devToken, m_tracker, &dq, 0, &msgBuf.timestamp);
}
enum {
VideoStatus_UNKNOWN = 0,
VideoStatus_NO_VIDEO_INPUT = 1,
VideoStatus_PORTRAIT_VIDEO_INPUT = 2,
VideoStatus_LANDSCAPE_VIDEO_INPUT = 3,
};
int videoStatus = VideoStatus_UNKNOWN;
if (ver >= 3)
{
// v3+: We've got status info in the upper nibble of the first byte.
bool gotVideo = (msgBuf.data[0] & 0x10) != 0; // got video?
bool gotPortrait = (msgBuf.data[0] & 0x20) != 0; // portrait mode?
if (!gotVideo) {
videoStatus = VideoStatus_NO_VIDEO_INPUT;
}
else {
if (gotPortrait) {
videoStatus = VideoStatus_PORTRAIT_VIDEO_INPUT;
}
else {
videoStatus = VideoStatus_LANDSCAPE_VIDEO_INPUT;
}
}
}
// Report the value of channel 0
osvrDeviceAnalogSetValueTimestamped(m_devToken, m_analog, ver, 0, &msgBuf.timestamp);
osvrDeviceAnalogSetValueTimestamped(m_devToken, m_analog, videoStatus, 1, &msgBuf.timestamp);
}
}
};
} // namespace
OSVR_PLUGIN(com_motionreality_OSVR_SharedIMU) {
osvr::pluginkit::PluginContext context(ctx);
std::cerr << "Loading plugin: com_motionreality_OSVR_SharedIMU" << std::endl;
/// Create our device object
static bool bOnce = false;
if (!bOnce)
{
osvr::pluginkit::registerObjectForDeletion(ctx, new OSVR_SharedIMU(ctx));
bOnce = true;
}
return OSVR_RETURN_SUCCESS;
}