vrpn 07.35
Virtual Reality Peripheral Network
Loading...
Searching...
No Matches
vrpn_Oculus.C
Go to the documentation of this file.
1
10// Based on the OSVR hacker dev kit driver by Kevin Godby.
11// Based on Oliver Kreylos' OculusRiftHIDReports.cpp and OculusRift.cpp.
12// He has given permission to release this code under the VRPN license:
13
14#include "vrpn_Oculus.h"
15
16#include "vrpn_BaseClass.h" // for ::vrpn_TEXT_NORMAL, etc
17
19
20#if defined(VRPN_USE_HID)
21
22// USB vendor and product IDs for the models we support
23static const vrpn_uint16 OCULUS_VENDOR = 0x2833;
24static const vrpn_uint16 DK1_PRODUCT = 0x0001;
25static const vrpn_uint16 DK2_PRODUCT = 0x0021;
26
27vrpn_Oculus::vrpn_Oculus(vrpn_uint16 product_id, vrpn_uint8 num_channels,
28 const char *name, vrpn_Connection *c, double keepAliveSeconds)
29 : vrpn_Analog(name, c)
30 , vrpn_HidInterface(m_filter =
31 new vrpn_HidProductAcceptor(OCULUS_VENDOR, product_id))
32{
33 d_keepAliveSeconds = keepAliveSeconds;
34
35 vrpn_Analog::num_channel = num_channels;
36
37 memset(channel, 0, sizeof(channel));
38 memset(last, 0, sizeof(last));
39
40 // Set the timestamp
43}
44
46{
47 try {
48 delete m_acceptor;
49 } catch (...) {
50 fprintf(stderr, "vrpn_Oculus::~vrpn_Oculus(): delete failed\n");
51 return;
52 }
53}
54
55// Copied from Oliver Kreylos' OculusRift.cpp; he has given permission
56// to release this code under the VRPN standard license.
57// Unpacks a 3D vector from 8 bytes
58inline void unpackVector(const vrpn_uint8 raw[8], int vector[3])
59{
60 // @todo Make this also work on big-endian architectures.
61 union // Helper union to assemble 3 or 4 bytes into a signed 32-bit integer
62 {
63 vrpn_uint8 b[4];
64 vrpn_int32 i;
65 } p;
66 struct // Helper structure to sign-extend a 21-bit signed integer value
67 {
68 signed int si : 21;
69 } s;
70
71 /* Assemble the vector's x component: */
72 p.b[0] = raw[2];
73 p.b[1] = raw[1];
74 p.b[2] = raw[0];
75 // p.b[3]=0U; // Not needed because it's masked out below anyway
76 vector[0] = s.si = (p.i >> 3) & 0x001fffff;
77
78 /* Assemble the vector's y component: */
79 p.b[0] = raw[5];
80 p.b[1] = raw[4];
81 p.b[2] = raw[3];
82 p.b[3] = raw[2];
83 vector[1] = s.si = (p.i >> 6) & 0x001fffff;
84
85 /* Assemble the vector's z component: */
86 p.b[0] = raw[7];
87 p.b[1] = raw[6];
88 p.b[2] = raw[5];
89 // p.b[3]=0U; // Not needed because it's masked out below anyway
90 vector[2] = s.si = (p.i >> 1) & 0x001fffff;
91}
92
93// Thank you to Oliver Kreylos for the info needed to write this function.
94// It is based on his OculusRiftHIDReports.cpp and OculusRift.cpp.
95
96void vrpn_Oculus::parse_message_type_1(std::size_t bytes,
97 vrpn_uint8 *buffer)
98{
99 size_t num_reports = buffer[1];
100 if (num_reports > 3) { num_reports = 3; }
101
102 // Skip past the report type and num_reports bytes and
103 // start parsing there.
104 vrpn_uint8 *bufptr = &buffer[2];
105
106 // The next two bytes are an increasing counter that changes by 1 for
107 // every report.
108 vrpn_uint16 report_index;
109 report_index = vrpn_unbuffer_from_little_endian<vrpn_uint16, vrpn_uint8>(bufptr);
110 channel[1] = report_index;
111
112 // The next two bytes are zero, so we skip them
113 vrpn_uint16 skip;
114 skip = vrpn_unbuffer_from_little_endian<vrpn_uint16, vrpn_uint8>(bufptr);
115
116 // The next entry is temperature, and it may be in hundredths of a degree C
117 vrpn_uint16 temperature;
118 const double temperature_scale = 0.01;
119 temperature = vrpn_unbuffer_from_little_endian<vrpn_uint16, vrpn_uint8>(bufptr);
120 channel[0] = temperature * temperature_scale;
121
122 // The magnetometer data comes after the space to store three
123 // reports.
124 vrpn_uint8 *magnetometer_ptr = &buffer[56];
125 vrpn_int16 magnetometer_raw[3];
126 for (size_t i = 0; i < 3; i++) {
127 magnetometer_raw[i] = vrpn_unbuffer_from_little_endian
128 <vrpn_int16, vrpn_uint8>(magnetometer_ptr);
129 }
130 // Invert all axes to make the magnetometer direction match
131 // the sign of the gravity vector.
132 const double magnetometer_scale = 0.0001;
133 channel[8] = -magnetometer_raw[0] * magnetometer_scale;
134 channel[9] = -magnetometer_raw[1] * magnetometer_scale;
135 channel[10] = -magnetometer_raw[2] * magnetometer_scale;
136
137 // Unpack a 16-byte accelerometer/gyro report using the routines from
138 // Oliver's code.
139 for (size_t i = 0; i < num_reports; i++) {
140 vrpn_int32 accelerometer_raw[3];
141 vrpn_int32 gyroscope_raw[3];
142 unpackVector(bufptr, accelerometer_raw);
143 bufptr += 8;
144 unpackVector(bufptr, gyroscope_raw);
145 bufptr += 8;
146
147 // Compute the double values using default calibration.
148 // The accelerometer data goes into analogs 0,1,2.
149 // The gyro data goes into analogs 3,4,5.
150 // The magnetomoter data goes into analogs 6,7,8.
151 const double accelerometer_scale = 0.0001;
152 const double gyroscope_scale = 0.0001;
153 channel[2] = accelerometer_raw[0] * accelerometer_scale;
154 channel[3] = accelerometer_raw[1] * accelerometer_scale;
155 channel[4] = accelerometer_raw[2] * accelerometer_scale;
156
157 channel[5] = gyroscope_raw[0] * gyroscope_scale;
158 channel[6] = gyroscope_raw[1] * gyroscope_scale;
159 channel[7] = gyroscope_raw[2] * gyroscope_scale;
160
162 }
163}
164
166{
168
169 // See if it has been long enough to send another keep-alive
174 }
175
176 update();
178}
179
180bool vrpn_Oculus::parse_message(std::size_t bytes,
181 vrpn_uint8 *buffer)
182{
183 return false;
184}
185
186void vrpn_Oculus::on_data_received(std::size_t bytes,
187 vrpn_uint8 *buffer)
188{
189 /* For debugging
190 printf("Got %d bytes:\n", static_cast<int>(bytes));
191 for (size_t i = 0; i < bytes; i++) {
192 printf("%02X ", buffer[i]);
193 }
194 printf("\n");
195 */
196
197 // Set the timestamp for all reports
199
200 // Make sure the message length and type is what we expect.
201 // We get 64-byte responses on Windows and 62-byte responses on the mac.
202 if ( (bytes != 62) && (bytes != 64) ) {
203 fprintf(stderr, "vrpn_Oculus::on_data_received(): Unexpected message length %d, ignoring\n",
204 static_cast<int>(bytes));
205 return;
206 }
207
208 switch(buffer[0]) {
209 case 1:
210 parse_message_type_1(bytes, buffer);
211 break;
212 default:
213 // Delegate message type to child
214 if (!parse_message(bytes, buffer)) {
215 fprintf(stderr, "vrpn_Oculus::on_data_received(): Unexpected message type %d, ignoring\n",
216 buffer[0]);
217 }
218 break;
219 }
220}
221
223 vrpn_Connection *c, double keepAliveSeconds)
224 : vrpn_Oculus(DK1_PRODUCT, 11,
225 name, c, keepAliveSeconds) {};
226
227// Thank you to Oliver Kreylos for the info needed to write this function.
228// It is based on his OculusRiftHIDReports.cpp, used with permission.
230 vrpn_uint16 interval
231 , vrpn_uint16 commandId)
232{
233 // Buffer to store our report in.
234 vrpn_uint8 pktBuffer[5];
235
236 /* Pack the packet buffer, using little-endian packing: */
237 vrpn_uint8 *bufptr = pktBuffer;
238 vrpn_int32 buflen = sizeof(pktBuffer);
239 vrpn_buffer_to_little_endian(&bufptr, &buflen, vrpn_uint8(0x08U));
240 vrpn_buffer_to_little_endian(&bufptr, &buflen, commandId);
241 vrpn_buffer_to_little_endian(&bufptr, &buflen, interval);
242
243 /* Write the feature report: */
244 send_feature_report(sizeof(pktBuffer), pktBuffer);
245}
246
248 const char *name, vrpn_Connection *c,
249 double keepAliveSeconds)
250 : vrpn_Oculus(DK2_PRODUCT, enableLEDs ? 12 : 11,
251 name, c, keepAliveSeconds)
252{
253 d_enableLEDs = enableLEDs;
254}
255
256bool vrpn_Oculus_DK2::parse_message(std::size_t bytes,
257 vrpn_uint8 *buffer)
258{
259 switch (buffer[0]) {
260 case 11:
261 parse_message_type_11(bytes, buffer);
262 break;
263 default:
264 return false;
265 }
266 return true;
267}
268
269// Thank you to Oliver Kreylos for the info needed to write this function.
270// The actual order and meaning of fields was determined by walking through
271// the packet to see what was in there, but the vector-decoding routines
272// are used to pull out the inertial sensor data.
273
275 vrpn_uint8 *buffer)
276{
277 // Started with the two different layouts in Oliver's code using my DK2
278 // and could not get the required data from it. I'm getting 64-byte
279 // reports that are somewhat like the 62-byte reports that code has but
280 // not the same. They seem closer to the code form the OculusRiftHIDReports.cpp
281 // document, but not the same (the last bytes are always 0, and they are supposed
282 // to be the magnetometer, for example).
283 // Looked at how the values changed and tried to figure out how to parse each
284 // part of the file.
285 // The first two bytes in the message are ignored; they are not present in the
286 // HID report for Oliver's code. The third byte is 0 and the fourth is the number
287 // of reports (up to 2, according to how much space is before the magnetometer
288 // data in the reports we found).
289 size_t num_reports = buffer[3];
290 if (num_reports > 2) { num_reports = 2; }
291
292 // Skip the first four bytes and start parsing the other reports from there.
293 vrpn_uint8 *bufptr = &buffer[4]; // Point at the start of the report
294
295 // The next two bytes seem to be an increasing counter that changes by 1 for
296 // every report.
297 vrpn_uint16 report_index, temperature;
298 report_index = vrpn_unbuffer_from_little_endian<vrpn_uint16, vrpn_uint8>(bufptr);
299 channel[1] = report_index;
300
301 // The next entry may be temperature, and it may be in hundredths of a degree C
302 const double temperature_scale = 0.01;
303 temperature = vrpn_unbuffer_from_little_endian<vrpn_uint16, vrpn_uint8>(bufptr);
304 channel[0] = temperature * temperature_scale;
305
306 // The next entry is a 4-byte counter with time since device was
307 // powered on in microseconds. We convert to seconds and report.
308 vrpn_uint32 microseconds;
309 microseconds = vrpn_unbuffer_from_little_endian<vrpn_uint32, vrpn_uint8>(bufptr);
310 channel[11] = microseconds * 1e-6;
311
312 // Unpack a 16-byte accelerometer/gyro report using the routines from
313 // Oliver's code. Also the magnetometer values(s).
314 // Then convert each to a scaled double value and send a report.
315 for (size_t i = 0; i < num_reports; i++) {
316 vrpn_int32 accelerometer_raw[3];
317 vrpn_int32 gyroscope_raw[3];
318 unpackVector(bufptr, accelerometer_raw);
319 bufptr += 8;
320 unpackVector(bufptr, gyroscope_raw);
321 bufptr += 8;
322
323 // Compute the double values using default calibration.
324 // The accelerometer data goes into analogs 0,1,2.
325 // The gyro data goes into analogs 3,4,5.
326 // The magnetomoter data goes into analogs 6,7,8.
327 const double accelerometer_scale = 0.0001;
328 const double gyroscope_scale = 0.0001;
329 channel[2] = accelerometer_raw[0] * accelerometer_scale;
330 channel[3] = accelerometer_raw[1] * accelerometer_scale;
331 channel[4] = accelerometer_raw[2] * accelerometer_scale;
332
333 channel[5] = gyroscope_raw[0] * gyroscope_scale;
334 channel[6] = gyroscope_raw[1] * gyroscope_scale;
335 channel[7] = gyroscope_raw[2] * gyroscope_scale;
336
337 // The magnetometer data comes after the space to store two
338 // reports. We don't know if there are two of them when
339 // there are two reports, but there is space in the report
340 // for it, so we try to decode two of them.
341 vrpn_uint8 *magnetometer_ptr = &buffer[44 + 8 * i];
342 vrpn_int16 magnetometer_raw[3];
343 for (size_t i = 0; i < 3; i++) {
344 magnetometer_raw[i] = vrpn_unbuffer_from_little_endian
345 <vrpn_int16, vrpn_uint8>(magnetometer_ptr);
346 }
347 // Invert these to make the magnetometer direction match
348 // the sign of the gravity vector.
349 const double magnetometer_scale = - 0.0001;
350 channel[8] = -magnetometer_raw[0] * magnetometer_scale;
351 channel[9] = -magnetometer_raw[1] * magnetometer_scale;
352 channel[10] = -magnetometer_raw[2] * magnetometer_scale;
353
355 }
356}
357
358
359// Thank you to Oliver Kreylos for the info needed to write this function.
360// It is based on his OculusRiftHIDReports.cpp, used with permission.
362 vrpn_uint16 interval
363 , vrpn_uint16 commandId)
364{
365 // Buffer to store our report in.
366 vrpn_uint8 pktBuffer[6];
367
368 /* Pack the packet buffer, using little-endian packing: */
369 vrpn_uint8 *bufptr = pktBuffer;
370 vrpn_int32 buflen = sizeof(pktBuffer);
371 vrpn_buffer_to_little_endian(&bufptr, &buflen, vrpn_uint8(0x11U));
372 vrpn_buffer_to_little_endian(&bufptr, &buflen, commandId);
373 vrpn_uint8 flags = d_enableLEDs ? 0x0bU : 0x01U;
374 vrpn_buffer_to_little_endian(&bufptr, &buflen, flags);
375 vrpn_buffer_to_little_endian(&bufptr, &buflen, interval);
376
377 /* Write the LED control feature report: */
378 send_feature_report(sizeof(pktBuffer), pktBuffer);
379}
380
381#endif
vrpn_float64 last[vrpn_CHANNEL_MAX]
Definition vrpn_Analog.h:39
vrpn_float64 channel[vrpn_CHANNEL_MAX]
Definition vrpn_Analog.h:38
vrpn_int32 num_channel
Definition vrpn_Analog.h:40
virtual void report_changes(vrpn_uint32 class_of_service=vrpn_CONNECTION_LOW_LATENCY, const struct timeval time=vrpn_ANALOG_NOW)
Send a report only if something has changed (for servers) Optionally, tell what time to stamp the val...
Definition vrpn_Analog.C:71
void server_mainloop(void)
Handles functions that all servers should provide in their mainloop() (ping/pong, for example) Should...
Generic connection class not specific to the transport mechanism.
void send_feature_report(size_t bytes, const vrpn_uint8 *buffer)
Call this to send a feature report to the device - first byte must be Report ID (or 0x0 for devices w...
vrpn_HidAcceptor * m_acceptor
This is the HidAcceptor we use when reconnecting.
virtual void update()
Polls the device buffers and causes on_data_received callbacks if appropriate You NEED to call this f...
Accepts any device with the given vendor and product IDs.
vrpn_Oculus_DK1(const char *name, vrpn_Connection *c=NULL, double keepAliveSeconds=9.0)
Constructor.
virtual void writeKeepAlive(vrpn_uint16 interval=10000, vrpn_uint16 commandId=0)
bool d_enableLEDs
Whether to trigger the LEDs.
bool parse_message(std::size_t bytes, vrpn_uint8 *buffer)
vrpn_Oculus_DK2(bool enableLEDs, const char *name, vrpn_Connection *c=NULL, double keepAliveSeconds=9.0)
Protected constructor so you can't instantiate this base class.
virtual void writeKeepAlive(vrpn_uint16 interval=10000, vrpn_uint16 commandId=0)
void parse_message_type_11(std::size_t bytes, vrpn_uint8 *buffer)
Parse and send reports for type-11 message.
Oculus Rift head-mounted display base class.
Definition vrpn_Oculus.h:21
void parse_message_type_1(std::size_t bytes, vrpn_uint8 *buffer)
Parse and send reports for type-1 message.
Definition vrpn_Oculus.C:96
struct timeval d_lastKeepAlive
Definition vrpn_Oculus.h:63
vrpn_Oculus(vrpn_uint16 product_id, vrpn_uint8 num_channels, const char *name, vrpn_Connection *c=NULL, double keepAliveSeconds=9.0)
Protected constructor so you can't instantiate this base class.
Definition vrpn_Oculus.C:27
double d_keepAliveSeconds
How often to send the keepAlive message to the Rift (triggers the LEDs if available)
Definition vrpn_Oculus.h:62
void on_data_received(std::size_t bytes, vrpn_uint8 *buffer)
Extracts the sensor values from each report and calls the appropriate parser.
virtual void mainloop()
Called once through each main loop iteration to handle updates. Remote object mainloop() should call ...
struct timeval d_timestamp
Timestamp updated during mainloop()
Definition vrpn_Oculus.h:58
virtual bool parse_message(std::size_t bytes, vrpn_uint8 *buffer)
virtual ~vrpn_Oculus()
Destructor.
Definition vrpn_Oculus.C:45
virtual void writeKeepAlive(vrpn_uint16 interval=10000, vrpn_uint16 commandId=0)=0
All types of client/server/peer objects in VRPN should be derived from the vrpn_BaseClass type descri...
#define VRPN_SUPPRESS_EMPTY_OBJECT_WARNING()
void unpackVector(const vrpn_uint8 raw[8], int vector[3])
Definition vrpn_Laputa.C:47
void unpackVector(const vrpn_uint8 raw[8], int vector[3])
Definition vrpn_Oculus.C:58
Header for various Oculus devices.
double vrpn_TimevalDurationSeconds(struct timeval endT, struct timeval startT)
Return the number of seconds between startT and endT as a floating-point value.
#define vrpn_gettimeofday
Definition vrpn_Shared.h:99
int vrpn_buffer_to_little_endian(ByteT **insertPt, vrpn_int32 *buflen, const T inVal)
Function template to buffer values to a buffer stored in little- endian order. Specify the type to bu...