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.
Definition: vrpn_Oculus.C:222
virtual void writeKeepAlive(vrpn_uint16 interval=10000, vrpn_uint16 commandId=0)
Definition: vrpn_Oculus.C:229
bool d_enableLEDs
Whether to trigger the LEDs.
Definition: vrpn_Oculus.h:120
bool parse_message(std::size_t bytes, vrpn_uint8 *buffer)
Definition: vrpn_Oculus.C:256
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.
Definition: vrpn_Oculus.C:247
virtual void writeKeepAlive(vrpn_uint16 interval=10000, vrpn_uint16 commandId=0)
Definition: vrpn_Oculus.C:361
void parse_message_type_11(std::size_t bytes, vrpn_uint8 *buffer)
Parse and send reports for type-11 message.
Definition: vrpn_Oculus.C:274
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.
Definition: vrpn_Oculus.C:186
virtual void mainloop()
Called once through each main loop iteration to handle updates. Remote object mainloop() should call ...
Definition: vrpn_Oculus.C:165
struct timeval d_timestamp
Timestamp updated during mainloop()
Definition: vrpn_Oculus.h:58
virtual bool parse_message(std::size_t bytes, vrpn_uint8 *buffer)
Definition: vrpn_Oculus.C:180
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.
Definition: vrpn_Shared.C:144
#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...
Definition: vrpn_Shared.h:415