vrpn  07.33
Virtual Reality Peripheral Network
vrpn_Saitek_Controller_Raw.C
Go to the documentation of this file.
1 // vrpn_Saitek_Controller_Raw.C: VRPN driver for Saitek 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 
9 
10 #if defined(VRPN_USE_HID)
11 
12 // USB vendor and product IDs for the models we support
13 static const vrpn_uint16 SAITEK_VENDOR = 0x06a3;
14 static const vrpn_uint16 ST290_PRO = 0x0d60;
15 
16 static const double POLL_INTERVAL = 1e+6 / 30.0; // If we have not heard, ask.
17 
18 #define GAMEPAD_TRIGGER_THRESHOLD 30
19 
21 // helpers
23 static vrpn_float64 normalize_dpad(unsigned char up, unsigned char right, unsigned char down, unsigned char left)
24 {
25  int x = 0;
26  int y = 0;
27  if (right)
28  {
29  x += 1;
30  }
31  if (left)
32  {
33  x -= 1;
34  }
35  if (up)
36  {
37  y += 1;
38  }
39  if (down)
40  {
41  y -= 1;
42  }
43  size_t index = ((x + 1) * 3) + (y + 1);
44  vrpn_float64 angles[] = {225, 270, 315, 180, -1, 0, 135, 90, 45};
45  return (angles[index]);
46 }
47 
48 static void normalize_axis(const unsigned int value, const short deadzone, const vrpn_float64 scale, vrpn_float64& channel, int wordSize = 16)
49 {
50  channel = (static_cast<float>(value) - (float) (1 << (wordSize - 1)));
51  if (fabs(channel) < (deadzone * 3 / 4))
52  {
53  channel = 0.0f;
54  }
55  else
56  {
57  channel /= (float) (1 << (wordSize - 1));
58  }
59  channel *= scale;
60  if (channel < -1.0) { channel = -1.0; }
61  if (channel > 1.0) { channel = 1.0; }
62 }
63 
64 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)
65 {
66  normalize_axis(x, deadzone, scale, channelX, wordSize);
67  normalize_axis(y, deadzone, scale, channelY, wordSize);
68 }
69 
70 static vrpn_float64 normalize_trigger(unsigned int trigger)
71 {
72  // Filter out low-intensity signals
73  int value = trigger - 0x80;
74  return ((fabs(static_cast<double>(value)) < GAMEPAD_TRIGGER_THRESHOLD) ? 0.0f : (value * 2.0f / 255.0f));
75 }
76 
78 // Common base class
81  vrpn_HidInterface(filter), vrpn_BaseClass(name, c), _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_Saitek_Controller_Raw::on_data_received(size_t bytes, vrpn_uint8 *buffer)
99 {
100  decodePacket(bytes, buffer);
101 }
102 
104 {
105  vrpn_Saitek_Controller_Raw* me = static_cast<vrpn_Saitek_Controller_Raw*>(thisPtr);
106  return (0);
107 }
108 
110 {
111  vrpn_Saitek_Controller_Raw* me = static_cast<vrpn_Saitek_Controller_Raw*>(thisPtr);
112  return (0);
113 }
114 
116 // ST290 Pro Joystick
119  vrpn_Saitek_Controller_Raw(_filter = new vrpn_HidProductAcceptor(SAITEK_VENDOR, ST290_PRO), 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_Saitek_ST290_Pro::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_Saitek_ST290_Pro::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_Saitek_ST290_Pro::decodePacket(size_t bytes, vrpn_uint8 *buffer)
188 {
189  // ST290 Pro 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)
196  [1]: Y-axis - lower byte (up=00, down=ff)
197  [2]: Z-rotate (left=00, right=ff)
198  [3]: Slider (up=00, down=ff)
199  [4]: buttons high nibble "1"=0x10, "2"=0x20, "3"=0x40, "4"=0x80, POV Hat low nibble (none=0x00, N=0x01, NE=0x02, ... NW=0x08)
200  [5]: 0xf0 high nibble, buttons low nibble (none=0x00, "5"=0x01, "6"=0x02)
201  */
202  // XXX Check to see that this works with HIDAPI, there may be two smaller reports.
203  if (bytes == 6)
204  {
205  normalize_axes(buffer[0], buffer[1], 0x08, 1.0f, channel[0], channel[1], 8);
206  normalize_axis(buffer[2], 0x08, 1.0f, channel[2], 8);
207  normalize_axis(buffer[3], 0x08, 1.0f, channel[3], 8);
208 
209  vrpn_uint8 value, mask;
210  value = (buffer[4] >> 4);
211  for (int btn = 0; btn < 4; btn++)
212  {
213  mask = static_cast<vrpn_uint8>(1 << (btn % 8));
214  buttons[btn] = ((value & mask) != 0);
215  }
216  value = (buffer[5] & 0x0f);
217  for (int btn = 0; btn < 4; btn++)
218  {
219  mask = static_cast<vrpn_uint8>(1 << (btn % 8));
220  buttons[btn + 4] = ((value & mask) != 0);
221  }
222 
223  // Point of View Hat
224  buttons[6] = buttons[7] = buttons[8] = buttons[9] = 0;
225  switch (buffer[4] & 0x0f)
226  {
227  case 1: // up
228  buttons[6] = true;
229  break;
230  case 2:
231  buttons[6] = buttons[7] = true;
232  break;
233  case 3: // right
234  buttons[7] = true;
235  break;
236  case 4:
237  buttons[7] = buttons[8] = true;
238  break;
239  case 5: // down
240  buttons[8] = true;
241  break;
242  case 6:
243  buttons[8] = buttons[9] = true;
244  break;
245  case 7: // left
246  buttons[9] = true;
247  break;
248  case 8:
249  buttons[9] = buttons[6] = true;
250  break;
251  case 0:
252  default:
253  // nothing to do
254  break;
255  }
256  channel[4] = normalize_dpad(buttons[7], buttons[8], buttons[9], buttons[10]);
257  }
258  else
259  {
260  fprintf(stderr, "vrpn_Saitek_ST290_Pro: Found a corrupted report; # total bytes = %u\n", static_cast<unsigned>(bytes));
261  }
262 }
263 
264 // End of VRPN_USE_HID
265 #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
vrpn_Saitek_Controller_Raw(vrpn_HidAcceptor *filter, const char *name, vrpn_Connection *c=0)
static int VRPN_CALLBACK on_last_disconnect(void *thisPtr, vrpn_HANDLERPARAM p)
vrpn_int32 num_buttons
Definition: vrpn_Button.h:47
vrpn_Saitek_ST290_Pro(const char *name, vrpn_Connection *c=0)
vrpn_float64 channel[vrpn_CHANNEL_MAX]
Definition: vrpn_Analog.h:38
virtual void decodePacket(size_t bytes, vrpn_uint8 *buffer)=0
void decodePacket(size_t bytes, vrpn_uint8 *buffer)
Accepts any device with the given vendor and product IDs.
struct timeval timestamp
Definition: vrpn_Dial.h:28
void on_data_received(size_t bytes, vrpn_uint8 *buffer)
Derived class reimplements this callback.
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()
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
static int VRPN_CALLBACK on_connect(void *thisPtr, vrpn_HANDLERPARAM p)
virtual void report(void)
Definition: vrpn_Dial.C:82
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
virtual void mainloop(void)
Called once through each main loop iteration to handle updates. Remote object mainloop() should call ...
struct timeval timestamp
Definition: vrpn_Button.h:48
#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
#define GAMEPAD_TRIGGER_THRESHOLD
unsigned char lastbuttons[vrpn_BUTTON_MAX_BUTTONS]
Definition: vrpn_Button.h:45
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
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