forked from vrpn/vrpn
-
Notifications
You must be signed in to change notification settings - Fork 1
/
vrpn_3Space.C
266 lines (236 loc) · 9.66 KB
/
vrpn_3Space.C
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
#include <ctype.h> // for isprint
#include <math.h> // for sqrt
#include <stdio.h> // for fprintf, stderr, perror, etc
#include "quat.h" // for Q_W, Q_X, Q_Y, Q_Z
#include "vrpn_3Space.h"
#include "vrpn_BaseClass.h" // for ::vrpn_TEXT_ERROR, etc
#include "vrpn_Serial.h" // for vrpn_write_characters, etc
#include "vrpn_Shared.h" // for vrpn_SleepMsecs, etc
#include "vrpn_Tracker.h" // for vrpn_TRACKER_FAIL, etc
#include "vrpn_Types.h" // for vrpn_int16, vrpn_float64
// This constant turns the tracker binary values in the range -32768 to
// 32768 to meters.
#define T_3_DATA_MAX (32768.0)
#define T_3_INCH_RANGE (65.48)
#define T_3_CM_RANGE (T_3_INCH_RANGE * 2.54)
#define T_3_METER_RANGE (T_3_CM_RANGE / 100.0)
#define T_3_BINARY_TO_METERS (T_3_METER_RANGE / T_3_DATA_MAX)
void vrpn_Tracker_3Space::reset()
{
int i,resetLen,ret;
unsigned char reset[10];
// Send the tracker a string that should reset it. The first time we
// try this, just do the normal ^Y reset. Later, try to reset
// to the factory defaults. Then toggle the extended mode.
// Then put in a carriage return to try and break it out of
// a query mode if it is in one. These additions are cumulative: by the
// end, we're doing them all.
resetLen = 0;
d_numResets++; // We're trying another reset
if (d_numResets > 1) { // Try to get it out of a query loop if its in one
reset[resetLen++] = (char) (13); // Return key -> get ready
}
if (d_numResets > 7) {
reset[resetLen++] = 'Y'; // Put tracker into tracking (not point) mode
}
if (d_numResets > 3) { // Get a little more aggressive
if (d_numResets > 4) { // Even more aggressive
reset[resetLen++] = 't'; // Toggle extended mode (in case it is on)
}
reset[resetLen++] = 'W'; // Reset to factory defaults
reset[resetLen++] = (char) (11); // Ctrl + k --> Burn settings into EPROM
}
reset[resetLen++] = (char) (25); // Ctrl + Y -> reset the tracker
send_text_message("Resetting", timestamp, vrpn_TEXT_ERROR, d_numResets);
for (i = 0; i < resetLen; i++) {
if (vrpn_write_characters(serial_fd, &reset[i], 1) == 1) {
vrpn_SleepMsecs(1000*2); // Wait 2 seconds each character
} else {
send_text_message("Failed writing to tracker", timestamp, vrpn_TEXT_ERROR, d_numResets);
perror("3Space: Failed writing to tracker");
status = vrpn_TRACKER_FAIL;
return;
}
}
vrpn_SleepMsecs(1000.0*10); // Sleep to let the reset happen
// Get rid of the characters left over from before the reset
vrpn_flush_input_buffer(serial_fd);
// Make sure that the tracker has stopped sending characters
vrpn_SleepMsecs(1000.0*2);
unsigned char scrap[80];
if ( (ret = vrpn_read_available_characters(serial_fd, scrap, 80)) != 0) {
fprintf(stderr," 3Space warning: got >=%d characters after reset:\n",ret);
for (i = 0; i < ret; i++) {
if (isprint(scrap[i])) {
fprintf(stderr,"%c",scrap[i]);
} else {
fprintf(stderr,"[0x%02X]",scrap[i]);
}
}
fprintf(stderr, "\n");
vrpn_flush_input_buffer(serial_fd); // Flush what's left
}
// Asking for tracker status
if (vrpn_write_characters(serial_fd, (const unsigned char *) "S", 1) == 1) {
vrpn_SleepMsecs(1000.0*1); // Sleep for a second to let it respond
} else {
perror(" 3Space write failed");
status = vrpn_TRACKER_FAIL;
return;
}
// Read Status
unsigned char statusmsg[56];
if ( (ret = vrpn_read_available_characters(serial_fd, statusmsg, 55)) != 55){
fprintf(stderr, " Got %d of 55 characters for status\n",ret);
}
if ( (statusmsg[0]!='2') || (statusmsg[54]!=(char)(10)) ) {
int i;
statusmsg[55] = '\0'; // Null-terminate the string
fprintf(stderr, " Tracker: status is (");
for (i = 0; i < 55; i++) {
if (isprint(statusmsg[i])) {
fprintf(stderr,"%c",statusmsg[i]);
} else {
fprintf(stderr,"[0x%02X]",statusmsg[i]);
}
}
fprintf(stderr, ")\n Bad status report from tracker, retrying reset\n");
return;
} else {
send_text_message("Got status (tracker back up)!", timestamp, vrpn_TEXT_ERROR, 0);
d_numResets = 0; // Success, use simple reset next time
}
// Set output format to be position,quaternion
// These are a capitol 'o' followed by comma-separated values that
// indicate data sets according to appendix F of the 3Space manual,
// then followed by character 13 (octal 15).
if (vrpn_write_characters(serial_fd, (const unsigned char *)"O2,11\015", 6) == 6) {
vrpn_SleepMsecs(1000.0*1); // Sleep for a second to let it respond
} else {
perror(" 3Space write failed");
status = vrpn_TRACKER_FAIL;
return;
}
// Set data format to BINARY mode
vrpn_write_characters(serial_fd, (const unsigned char *)"f", 1);
// Set tracker to continuous mode
if (vrpn_write_characters(serial_fd,(const unsigned char *) "C", 1) != 1)
perror(" 3Space write failed");
else {
fprintf(stderr, " 3Space set to continuous mode\n");
}
fprintf(stderr, " (at the end of 3Space reset routine)\n");
vrpn_gettimeofday(×tamp, NULL); // Set watchdog now
status = vrpn_TRACKER_SYNCING; // We're trying for a new reading
}
int vrpn_Tracker_3Space::get_report(void)
{
int ret;
// The reports are each 20 characters long, and each start with a
// byte that has the high bit set and no other bytes have the high
// bit set. If we're synching, read a byte at a time until we find
// one with the high bit set.
if (status == vrpn_TRACKER_SYNCING) {
// Try to get a character. If none, just return.
if (vrpn_read_available_characters(serial_fd, buffer, 1) != 1) {
return 0;
}
// If the high bit isn't set, we don't want it we
// need to look at the next one, so just return
if ( (buffer[0] & 0x80) == 0) {
send_text_message("Syncing (high bit not set)", timestamp, vrpn_TEXT_WARNING);
return 0;
}
// Got the first character of a report -- go into PARTIAL mode
// and say that we got one character at this time.
bufcount = 1;
vrpn_gettimeofday(×tamp, NULL);
status = vrpn_TRACKER_PARTIAL;
}
// Read as many bytes of this 20 as we can, storing them
// in the buffer. We keep track of how many have been read so far
// and only try to read the rest. The routine that calls this one
// makes sure we get a full reading often enough (ie, it is responsible
// for doing the watchdog timing to make sure the tracker hasn't simply
// stopped sending characters).
ret = vrpn_read_available_characters(serial_fd, &buffer[bufcount],
20-bufcount);
if (ret == -1) {
send_text_message("Error reading, resetting", timestamp, vrpn_TEXT_ERROR);
status = vrpn_TRACKER_FAIL;
return 0;
}
bufcount += ret;
if (bufcount < 20) { // Not done -- go back for more
return 0;
}
{ // Decode the report
unsigned char decode[17];
int i;
const unsigned char mask[8] = {0x01, 0x02, 0x04, 0x08,
0x10, 0x20, 0x40, 0x80 };
// Clear the MSB in the first byte
buffer[0] &= 0x7F;
// Decode the 3Space binary representation into standard
// 8-bit bytes. This is done according to page 4-4 of the
// 3Space user's manual, which says that the high-order bits
// of each group of 7 bytes is packed into the 8th byte of the
// group. Decoding involves setting those bits in the bytes
// iff their encoded counterpart is set and then skipping the
// byte that holds the encoded bits.
// We decode from buffer[] into decode[] (which is 3 bytes
// shorter due to the removal of the bit-encoding bytes).
// decoding from buffer[0-6] into decode[0-6]
for (i=0; i<7; i++) {
decode[i] = buffer[i];
if ( (buffer[7] & mask[i]) != 0) {
decode[i] |= (unsigned char)(0x80);
}
}
// decoding from buffer[8-14] into decode[7-13]
for (i=7; i<14; i++) {
decode[i] = buffer[i+1];
if ( (buffer[15] & mask[i-7]) != 0) {
decode[i] |= (unsigned char)(0x80);
}
}
// decoding from buffer[16-18] into decode[14-16]
for (i=14; i<17; i++) {
decode[i] = buffer[i+2];
if ( (buffer[19] & mask[i-14]) != 0) {
decode[i] |= (unsigned char)(0x80);
}
}
// Parse out sensor number, which is the second byte and is
// stored as the ASCII number of the sensor, with numbers
// starting from '1'. We turn it into a zero-based unit number.
d_sensor = decode[1] - '1';
// Position
unsigned char * unbufPtr = &decode[3];
pos[0] = vrpn_unbuffer_from_little_endian<vrpn_int16>(unbufPtr) * T_3_BINARY_TO_METERS;
pos[1] = vrpn_unbuffer_from_little_endian<vrpn_int16>(unbufPtr) * T_3_BINARY_TO_METERS;
pos[2] = vrpn_unbuffer_from_little_endian<vrpn_int16>(unbufPtr) * T_3_BINARY_TO_METERS;
// Quarternion orientation. The 3Space gives quaternions
// as w,x,y,z while the VR code handles them as x,y,z,w,
// so we need to switch the order when decoding. Also the
// tracker does not normalize the quaternions.
d_quat[Q_W] = vrpn_unbuffer_from_little_endian<vrpn_int16>(unbufPtr);
d_quat[Q_X] = vrpn_unbuffer_from_little_endian<vrpn_int16>(unbufPtr);
d_quat[Q_Y] = vrpn_unbuffer_from_little_endian<vrpn_int16>(unbufPtr);
d_quat[Q_Z] = vrpn_unbuffer_from_little_endian<vrpn_int16>(unbufPtr);
//Normalize quaternion
double norm = sqrt ( d_quat[0]*d_quat[0] + d_quat[1]*d_quat[1]
+ d_quat[2]*d_quat[2] + d_quat[3]*d_quat[3]);
for (i=0; i<4; i++) {
d_quat[i] /= norm;
}
// Done with the decoding, set the report to ready
// Ready for another report
status = vrpn_TRACKER_SYNCING;
bufcount = 0;
}
return 1; // Got a report.
#ifdef VERBOSE
print_latest_report();
#endif
}