vrpn  07.33
Virtual Reality Peripheral Network
vrpn_CHProducts_Controller_Raw.C
Go to the documentation of this file.
1 // vrpn_CHProducts_Controller_Raw.C: VRPN driver for CHProducts Controller Raw devices
2 
3 #include <stdio.h> // for fprintf, stderr, NULL
4 #include <string.h> // for memset
5 #include <math.h> // for sqrt and fabs
6 
8 
10 
11 #if defined(VRPN_USE_HID)
12 
13 // USB vendor and product IDs for the models we support
14 static const vrpn_uint16 CHPRODUCTS_VENDOR = 0x068e;
15 static const vrpn_uint16 FIGHTERSTICK_USB = 0x00f3;
16 
17 static const double POLL_INTERVAL = 1e+6 / 30.0; // If we have not heard, ask.
18 
19 #define GAMEPAD_TRIGGER_THRESHOLD 30
20 
22 // helpers
24 
25 static vrpn_float64 normalize_dpad(unsigned char up, unsigned char right, unsigned char down, unsigned char left)
26 {
27  int x = 0;
28  int y = 0;
29  if (right) {
30  x += 1;
31  }
32  if (left) {
33  x -= 1;
34  }
35  if (up) {
36  y += 1;
37  }
38  if (down) {
39  y -= 1;
40  }
41  size_t index = ((x + 1) * 3) + (y + 1);
42  vrpn_float64 angles[] = {225, 270, 315, 180, -1, 0, 135, 90, 45};
43  return (angles[index]);
44 }
45 
46 static void normalize_axis(const unsigned int value, const short deadzone, const vrpn_float64 scale, vrpn_float64& channel, int wordSize = 16)
47 {
48  channel = (static_cast<float>(value) - (float) (1 << (wordSize - 1)));
49  if (fabs(channel) < (deadzone * 3 / 4))
50  {
51  channel = 0.0f;
52  }
53  else
54  {
55  channel /= (float) (1 << (wordSize - 1));
56  }
57  channel *= scale;
58  if (channel < -1.0) { channel = -1.0; }
59  if (channel > 1.0) { channel = 1.0; }
60 }
61 
62 static void normalize_axes(const unsigned int x, const unsigned int y, const short deadzone, const vrpn_float64 scale, vrpn_float64& channelX, vrpn_float64& channelY, int wordSize = 16)
63 {
64  normalize_axis(x, deadzone, scale, channelX, wordSize);
65  normalize_axis(y, deadzone, scale, channelY, wordSize);
66 }
67 
68 static vrpn_float64 normalize_trigger(unsigned int trigger)
69 {
70  // Filter out low-intensity signals
71  int value = trigger - 0x80;
72  return ((fabs(static_cast<double>(value)) < GAMEPAD_TRIGGER_THRESHOLD) ? 0.0f : (value * 2.0f / 255.0f));
73 }
74 
76 // Common base class
79  vrpn_HidInterface(filter)
80  , vrpn_BaseClass(name, c)
81  , _filter(filter)
82 {
83  init_hid();
84 }
85 
87 {
88  delete _filter;
89 }
90 
92 {
93  // Get notifications when clients connect and disconnect
96 }
97 
98 void vrpn_CHProducts_Controller_Raw::on_data_received(size_t bytes, vrpn_uint8 *buffer)
99 {
100  decodePacket(bytes, buffer);
101 }
102 
104 {
106  return (0);
107 }
108 
110 {
112  return (0);
113 }
114 
116 // ST290 Pro Joystick
119  vrpn_CHProducts_Controller_Raw(_filter = new vrpn_HidProductAcceptor(CHPRODUCTS_VENDOR, FIGHTERSTICK_USB), name, c),
120  vrpn_Button_Filter(name, c), vrpn_Analog(name, c), vrpn_Dial(name, c)
121 {
125 
126  // Initialize the state of all the analogs, buttons, and dials
127  memset(buttons, 0, sizeof(buttons));
128  memset(lastbuttons, 0, sizeof(lastbuttons));
129  memset(channel, 0, sizeof(channel));
130  memset(last, 0, sizeof(last));
131 }
132 
134 {
135  update();
136  server_mainloop();
137  struct timeval current_time;
138  vrpn_gettimeofday(&current_time, NULL);
139  if (vrpn_TimevalDuration(current_time, _timestamp) > POLL_INTERVAL)
140  {
141  _timestamp = current_time;
142  report_changes();
143 
146  if (vrpn_Dial::num_dials > 0)
147  {
149  }
150  }
151 }
152 
153 void vrpn_CHProducts_Fighterstick_USB::report(vrpn_uint32 class_of_service)
154 {
157  if (vrpn_Dial::num_dials > 0)
158  {
160  }
161 
162  vrpn_Analog::report_changes(class_of_service);
164  if (vrpn_Dial::num_dials > 0)
165  {
167  }
168 }
169 
170 void vrpn_CHProducts_Fighterstick_USB::report_changes(vrpn_uint32 class_of_service)
171 {
174  if (vrpn_Dial::num_dials > 0)
175  {
177  }
178 
179  vrpn_Analog::report(class_of_service);
181  if (vrpn_Dial::num_dials > 0)
182  {
184  }
185 }
186 
187 void vrpn_CHProducts_Fighterstick_USB::decodePacket(size_t bytes, vrpn_uint8 *buffer)
188 {
189  // Fighterstick USB joystick
190 
191  // Decode all full reports, each of which is 6 bytes long.
192  // Because there is only one type of report, the initial "0" report-type
193  // byte is removed by the HIDAPI driver.
194  /*
195  [0]: X-axis (left=00, right=ff, center=80)
196  [1]: Y-axis (up=00, down=ff, center=80)
197  [2]: throttle wheel (up=00, down=ff)
198  [3]: buttons high nibble: 0x10=trigger, 0x20=top red, 0x40=upper index red (toggles buttons 17-19), 0x80=pinky red,
199  - - low nibble 8-way POV hat upper-right on top: none=0x00, N=0x01, NE=0x02, ... NW=0x08
200  [4]: high nibble 4-way hat #2 (lower-right on top): none=0x00, N=0x10, E=0x20, S=0x40, W=0x80
201  - - low nibble 4-way hat #1 (left on top): none=0x00, N=0x01, E=0x02, S=0x04, W=0x08
202  [5]: upper nibble mode bits: 0x10=green, 0x20=red, 0x40=yellow, 0x80=<not used>
203  - - low nibble 4-way hat #3 (thumb): none=0x00, N=0x01, E=0x02, S=0x04, W=0x08
204  */
205  // XXX Check to see that this works with HIDAPI, there may be two smaller reports.
206  if (bytes == 6)
207  {
208  normalize_axes(buffer[0], buffer[1], 0x08, 1.0f, channel[0], channel[1], 8);
209  normalize_axis(buffer[2], 0x08, 1.0f, channel[2], 8);
210 
211  vrpn_uint8 value, mask;
212  value = (buffer[3] >> 4);
213  for (int btn = 0; btn < 4; btn++)
214  {
215  mask = static_cast<vrpn_uint8>(1 << (btn % 8));
216  buttons[btn] = ((value & mask) != 0);
217  }
218 
219  // Point of View Hat
220  buttons[4] = buttons[5] = buttons[6] = buttons[7] = 0;
221  switch (buffer[3] & 0x0f)
222  {
223  case 1: // up
224  buttons[4] = true;
225  break;
226  case 2:
227  buttons[4] = buttons[5] = true;
228  break;
229  case 3: // right
230  buttons[5] = true;
231  break;
232  case 4:
233  buttons[5] = buttons[6] = true;
234  break;
235  case 5: // down
236  buttons[6] = true;
237  break;
238  case 6:
239  buttons[6] = buttons[7] = true;
240  break;
241  case 7: // left
242  buttons[7] = true;
243  break;
244  case 8:
245  buttons[7] = buttons[4] = true;
246  break;
247  case 0:
248  default:
249  // nothing to do
250  break;
251  }
252  channel[3] = normalize_dpad(buttons[4], buttons[5], buttons[6], buttons[7]);
253 
254  // 4-way Hat #2
255  buttons[8] = buttons[9] = buttons[10] = buttons[11] = 0;
256  switch (buffer[4] >> 4)
257  {
258  case 1: // up
259  buttons[8] = true;
260  break;
261  case 2: // right
262  buttons[9] = true;
263  break;
264  case 4: // down
265  buttons[10] = true;
266  break;
267  case 8: // left
268  buttons[11] = true;
269  break;
270  case 0:
271  default:
272  // nothing to do
273  break;
274  }
275  channel[4] = normalize_dpad(buttons[8], buttons[9], buttons[10], buttons[11]);
276  // 4-way Hat #1
277  buttons[12] = buttons[13] = buttons[14] = buttons[15] = 0;
278  switch (buffer[4] & 0x0f)
279  {
280  case 1: // up
281  buttons[12] = true;
282  break;
283  case 2: // right
284  buttons[13] = true;
285  break;
286  case 4: // down
287  buttons[14] = true;
288  break;
289  case 8: // left
290  buttons[15] = true;
291  break;
292  case 0:
293  default:
294  // nothing to do
295  break;
296  }
297  channel[5] = normalize_dpad(buttons[12], buttons[13], buttons[14], buttons[15]);
298 
299  // mode
300  // keep pseudo-button pressed to indicate mode
301  //buttons[16] = buttons[17] = buttons[18] = buttons[19] = 0;
302  switch (buffer[5] >> 4)
303  {
304  case 1: // green
305  buttons[16] = true;
306  buttons[17] = buttons[18] = false;
307  break;
308  case 2: // red
309  buttons[17] = true;
310  buttons[16] = buttons[18] = false;
311  break;
312  case 4: // yellow
313  buttons[18] = true;
314  buttons[16] = buttons[17] = false;
315  break;
316 /*
317  case 8: // none
318  buttons[19] = true;
319  break;
320 */
321  case 0:
322  default:
323  // nothing to do
324  break;
325  }
326  if (buttons[16] || buttons[17] || buttons[18] || buttons[19])
327  {
328  channel[6] = (normalize_dpad(buttons[16], buttons[17], buttons[18], buttons[19]) / 90.0f) + 1.0f;
329  }
330  // 4-way Hat #3
331  buttons[20] = buttons[21] = buttons[22] = buttons[23] = 0;
332  switch (buffer[5] & 0x0f)
333  {
334  case 1: // up
335  buttons[20] = true;
336  break;
337  case 2: // right
338  buttons[21] = true;
339  break;
340  case 4: // down
341  buttons[22] = true;
342  break;
343  case 8: // left
344  buttons[23] = true;
345  break;
346  case 0:
347  default:
348  // nothing to do
349  break;
350  }
351  channel[7] = normalize_dpad(buttons[20], buttons[21], buttons[22], buttons[23]);
352  }
353  else
354  {
355  fprintf(stderr, "vrpn_CHProducts_Fighterstick_USB: Found a corrupted report; # total bytes = %u\n", static_cast<unsigned>(bytes));
356  }
357 }
358 
359 // End of VRPN_USE_HID
360 #endif
void server_mainloop(void)
Handles functions that all servers should provide in their mainloop() (ping/pong, for example) Should...
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
static int VRPN_CALLBACK on_last_disconnect(void *thisPtr, vrpn_HANDLERPARAM p)
vrpn_int32 num_buttons
Definition: vrpn_Button.h:47
virtual void decodePacket(size_t bytes, vrpn_uint8 *buffer)=0
virtual void mainloop(void)
Called once through each main loop iteration to handle updates. Remote object mainloop() should call ...
Accepts any device with the given vendor and product IDs.
struct timeval timestamp
Definition: vrpn_Dial.h:28
Generic connection class not specific to the transport mechanism.
vrpn_int32 num_dials
Definition: vrpn_Dial.h:27
virtual void report(vrpn_uint32 class_of_service=vrpn_CONNECTION_LOW_LATENCY, const struct timeval time=vrpn_ANALOG_NOW)
Send a report whether something has changed or not (for servers) Optionally, tell what time to stamp ...
Definition: vrpn_Analog.C:94
#define VRPN_SUPPRESS_EMPTY_OBJECT_WARNING()
#define GAMEPAD_TRIGGER_THRESHOLD
const char * vrpn_dropped_last_connection
virtual void report_changes(void)
Definition: vrpn_Button.C:382
virtual void report_changes(void)
Definition: vrpn_Button.C:422
vrpn_int32 num_channel
Definition: vrpn_Analog.h:40
virtual void report(void)
Definition: vrpn_Dial.C:82
static int VRPN_CALLBACK on_connect(void *thisPtr, vrpn_HANDLERPARAM p)
int register_autodeleted_handler(vrpn_int32 type, vrpn_MESSAGEHANDLER handler, void *userdata, vrpn_int32 sender=vrpn_ANY_SENDER)
Registers a handler with the connection, and remembers to delete at destruction.
vrpn_Connection * d_connection
Connection that this object talks to.
This structure is what is passed to a vrpn_Connection message callback.
#define POLL_INTERVAL
Definition: vrpn_IDEA.C:26
const char * vrpn_got_connection
struct timeval timestamp
Definition: vrpn_Button.h:48
void on_data_received(size_t bytes, vrpn_uint8 *buffer)
Derived class reimplements this callback.
#define vrpn_gettimeofday
Definition: vrpn_Shared.h:89
virtual void update()
Polls the device buffers and causes on_data_received callbacks if appropriate You NEED to call this f...
Class from which all user-level (and other) classes that communicate with vrpn_Connections should der...
All button servers should derive from this class, which provides the ability to turn any of the butto...
Definition: vrpn_Button.h:65
vrpn_CHProducts_Controller_Raw(vrpn_HidAcceptor *filter, const char *name, vrpn_Connection *c=0)
unsigned char lastbuttons[vrpn_BUTTON_MAX_BUTTONS]
Definition: vrpn_Button.h:45
vrpn_CHProducts_Fighterstick_USB(const char *name, vrpn_Connection *c=0)
unsigned char buttons[vrpn_BUTTON_MAX_BUTTONS]
Definition: vrpn_Button.h:44
unsigned long vrpn_TimevalDuration(struct timeval endT, struct timeval startT)
Return number of microseconds between startT and endT.
Definition: vrpn_Shared.C:129
void decodePacket(size_t bytes, vrpn_uint8 *buffer)
virtual vrpn_int32 register_message_type(const char *name)
struct timeval timestamp
Definition: vrpn_Analog.h:41
vrpn_float64 last[vrpn_CHANNEL_MAX]
Definition: vrpn_Analog.h:39