11 #include "definitions.h"
12 #include "wiiuse_internal.h"
15 static int ir_correct_for_bounds(float* x
, float* y
, enum aspect_t aspect
, int offset_x
, int offset_y
);
16 static void ir_convert_to_vres(float* x
, float* y
, enum aspect_t aspect
, unsigned int vx
, unsigned int vy
);
19 * @brief Get the IR sensitivity settings.
21 * @param wm Pointer to a wiimote_t structure.
22 * @param block1 [out] Pointer to where block1 will be set.
23 * @param block2 [out] Pointer to where block2 will be set.
25 * @return Returns the sensitivity level.
27 static int get_ir_sens(struct wiimote_t
* wm
, char** block1
, char** block2
) {
28 if (WIIMOTE_IS_SET(wm
, WIIMOTE_STATE_IR_SENS_LVL1
)) {
29 *block1
= WM_IR_BLOCK1_LEVEL1
;
30 *block2
= WM_IR_BLOCK2_LEVEL1
;
32 } else if (WIIMOTE_IS_SET(wm
, WIIMOTE_STATE_IR_SENS_LVL2
)) {
33 *block1
= WM_IR_BLOCK1_LEVEL2
;
34 *block2
= WM_IR_BLOCK2_LEVEL2
;
36 } else if (WIIMOTE_IS_SET(wm
, WIIMOTE_STATE_IR_SENS_LVL3
)) {
37 *block1
= WM_IR_BLOCK1_LEVEL3
;
38 *block2
= WM_IR_BLOCK2_LEVEL3
;
40 } else if (WIIMOTE_IS_SET(wm
, WIIMOTE_STATE_IR_SENS_LVL4
)) {
41 *block1
= WM_IR_BLOCK1_LEVEL4
;
42 *block2
= WM_IR_BLOCK2_LEVEL4
;
44 } else if (WIIMOTE_IS_SET(wm
, WIIMOTE_STATE_IR_SENS_LVL5
)) {
45 *block1
= WM_IR_BLOCK1_LEVEL5
;
46 *block2
= WM_IR_BLOCK2_LEVEL5
;
55 static void rotate_dots(struct fdot_t
* in
, struct fdot_t
*out
, int count
, float ang
) {
60 for (i
= 0; i
< count
; ++i
) {
67 s
= sin(DEGREE_TO_RAD(ang
));
68 c
= cos(DEGREE_TO_RAD(ang
));
71 * [ cos(theta) -sin(theta) ][ ir->rx ]
72 * [ sin(theta) cos(theta) ][ ir->ry ]
75 for (i
= 0; i
< count
; ++i
) {
76 out
[i
].x
= (c
* in
[i
].x
) + (-s
* in
[i
].y
);
77 out
[i
].y
= (s
* in
[i
].x
) + (c
* in
[i
].y
);
82 * @brief Correct for the IR bounding box.
84 * @param x [out] The current X, it will be updated if valid.
85 * @param y [out] The current Y, it will be updated if valid.
86 * @param aspect Aspect ratio of the screen.
87 * @param offset_x The X offset of the bounding box.
88 * @param offset_y The Y offset of the bounding box.
90 * @return Returns 1 if the point is valid and was updated.
92 * Nintendo was smart with this bit. They sacrifice a little
93 * precision for a big increase in usability.
95 static int ir_correct_for_bounds(float* x
, float* y
, enum aspect_t aspect
, int offset_x
, int offset_y
) {
99 if (aspect
== WIIUSE_ASPECT_16_9
) {
100 xs
= WM_ASPECT_16_9_X
;
101 ys
= WM_ASPECT_16_9_Y
;
103 xs
= WM_ASPECT_4_3_X
;
104 ys
= WM_ASPECT_4_3_Y
;
107 x0
= ((1024 - xs
) / 2) + offset_x
;
108 y0
= ((768 - ys
) / 2) + offset_y
;
113 && (*y
<= (y0
+ ys
)))
126 * @brief Interpolate the point to the user defined virtual screen resolution.
128 static void ir_convert_to_vres(float* x
, float* y
, enum aspect_t aspect
, unsigned int vx
, unsigned int vy
) {
131 if (aspect
== WIIUSE_ASPECT_16_9
) {
132 xs
= WM_ASPECT_16_9_X
;
133 ys
= WM_ASPECT_16_9_Y
;
135 xs
= WM_ASPECT_4_3_X
;
136 ys
= WM_ASPECT_4_3_Y
;
142 *x
= (*x
/ (float)xs
) * vx
;
143 *y
= (*y
/ (float)ys
) * vy
;
146 void wiiuse_set_ir_mode(struct wiimote_t
*wm
)
151 if(!WIIMOTE_IS_SET(wm
,WIIMOTE_STATE_IR
)) return;
153 if(WIIMOTE_IS_SET(wm
,WIIMOTE_STATE_EXP
)) buf
= WM_IR_TYPE_BASIC
;
154 else buf
= WM_IR_TYPE_EXTENDED
;
155 wiiuse_write_data(wm
,WM_REG_IR_MODENUM
, &buf
, 1, NULL
);
158 void wiiuse_set_ir(struct wiimote_t
*wm
,int status
)
168 * Wait for the handshake to finish first.
169 * When it handshake finishes and sees that
170 * IR is enabled, it will call this function
171 * again to actually enable IR.
173 if(!WIIMOTE_IS_SET(wm
,WIIMOTE_STATE_HANDSHAKE_COMPLETE
)) {
174 WIIUSE_DEBUG("Tried to enable IR, will wait until handshake finishes.\n");
176 WIIMOTE_ENABLE_STATE(wm
, WIIMOTE_STATE_IR_INIT
);
178 WIIMOTE_DISABLE_STATE(wm
, WIIMOTE_STATE_IR_INIT
);
183 * Check to make sure a sensitivity setting is selected.
185 ir_level
= get_ir_sens(wm
, &block1
, &block2
);
187 WIIUSE_ERROR("No IR sensitivity setting selected.");
192 /* if already enabled then stop */
193 if (WIIMOTE_IS_SET(wm
, WIIMOTE_STATE_IR
)) {
194 wiiuse_status(wm
,NULL
);
198 /* if already disabled then stop */
199 if (!WIIMOTE_IS_SET(wm
, WIIMOTE_STATE_IR
)) {
200 wiiuse_status(wm
,NULL
);
205 buf
= (status
? 0x04 : 0x00);
206 wiiuse_sendcmd(wm
,WM_CMD_IR
,&buf
,1,NULL
);
207 wiiuse_sendcmd(wm
,WM_CMD_IR_2
,&buf
,1,NULL
);
210 WIIUSE_DEBUG("Disabled IR cameras for wiimote id %i.", wm
->unid
);
211 wiiuse_status(wm
,NULL
);
215 /* enable IR, set sensitivity */
217 wiiuse_write_data(wm
,WM_REG_IR
,&buf
,1,NULL
);
219 wiiuse_write_data(wm
, WM_REG_IR_BLOCK1
, (ubyte
*)block1
, 9, NULL
);
220 wiiuse_write_data(wm
, WM_REG_IR_BLOCK2
, (ubyte
*)block2
, 2, NULL
);
222 if(WIIMOTE_IS_SET(wm
,WIIMOTE_STATE_EXP
)) buf
= WM_IR_TYPE_BASIC
;
223 else buf
= WM_IR_TYPE_EXTENDED
;
224 wiiuse_write_data(wm
,WM_REG_IR_MODENUM
, &buf
, 1, NULL
);
226 wiiuse_status(wm
,NULL
);
231 * @brief Set the virtual screen resolution for IR tracking.
233 * @param wm Pointer to a wiimote_t structure.
234 * @param status 1 to enable, 0 to disable.
236 void wiiuse_set_ir_vres(struct wiimote_t
* wm
, unsigned int x
, unsigned int y
) {
239 wm
->ir
.vres
[0] = (x
-1);
240 wm
->ir
.vres
[1] = (y
-1);
244 * @brief Set the XY position for the IR cursor.
246 * @param wm Pointer to a wiimote_t structure.
248 void wiiuse_set_ir_position(struct wiimote_t
* wm
, enum ir_position_t pos
) {
255 case WIIUSE_IR_ABOVE
:
256 wm
->ir
.offset
[0] = 0;
258 if (wm
->ir
.aspect
== WIIUSE_ASPECT_16_9
)
259 wm
->ir
.offset
[1] = WM_ASPECT_16_9_Y
/2 - 70;
260 else if (wm
->ir
.aspect
== WIIUSE_ASPECT_4_3
)
261 wm
->ir
.offset
[1] = WM_ASPECT_4_3_Y
/2 - 100;
265 case WIIUSE_IR_BELOW
:
266 wm
->ir
.offset
[0] = 0;
268 if (wm
->ir
.aspect
== WIIUSE_ASPECT_16_9
)
269 wm
->ir
.offset
[1] = -WM_ASPECT_16_9_Y
/2 + 70;
270 else if (wm
->ir
.aspect
== WIIUSE_ASPECT_4_3
)
271 wm
->ir
.offset
[1] = -WM_ASPECT_4_3_Y
/2 + 100;
281 * @brief Set the aspect ratio of the TV/monitor.
283 * @param wm Pointer to a wiimote_t structure.
284 * @param aspect Either WIIUSE_ASPECT_16_9 or WIIUSE_ASPECT_4_3
286 void wiiuse_set_aspect_ratio(struct wiimote_t
* wm
, enum aspect_t aspect
) {
289 wm
->ir
.aspect
= aspect
;
291 if (aspect
== WIIUSE_ASPECT_4_3
) {
292 wm
->ir
.vres
[0] = WM_ASPECT_4_3_X
;
293 wm
->ir
.vres
[1] = WM_ASPECT_4_3_Y
;
295 wm
->ir
.vres
[0] = WM_ASPECT_16_9_X
;
296 wm
->ir
.vres
[1] = WM_ASPECT_16_9_Y
;
299 /* reset the position offsets */
300 wiiuse_set_ir_position(wm
, wm
->ir
.pos
);
305 * @brief Set the IR sensitivity.
307 * @param wm Pointer to a wiimote_t structure.
308 * @param level 1-5, same as Wii system sensitivity setting.
310 * If the level is < 1, then level will be set to 1.
311 * If the level is > 5, then level will be set to 5.
313 void wiiuse_set_ir_sensitivity(struct wiimote_t
* wm
, int level
) {
319 if (level
> 5) level
= 5;
320 if (level
< 1) level
= 1;
322 WIIMOTE_DISABLE_STATE(wm
, (WIIMOTE_STATE_IR_SENS_LVL1
|
323 WIIMOTE_STATE_IR_SENS_LVL2
|
324 WIIMOTE_STATE_IR_SENS_LVL3
|
325 WIIMOTE_STATE_IR_SENS_LVL4
|
326 WIIMOTE_STATE_IR_SENS_LVL5
));
330 WIIMOTE_ENABLE_STATE(wm
, WIIMOTE_STATE_IR_SENS_LVL1
);
333 WIIMOTE_ENABLE_STATE(wm
, WIIMOTE_STATE_IR_SENS_LVL2
);
336 WIIMOTE_ENABLE_STATE(wm
, WIIMOTE_STATE_IR_SENS_LVL3
);
339 WIIMOTE_ENABLE_STATE(wm
, WIIMOTE_STATE_IR_SENS_LVL4
);
342 WIIMOTE_ENABLE_STATE(wm
, WIIMOTE_STATE_IR_SENS_LVL5
);
348 if(!WIIMOTE_IS_SET(wm
,WIIMOTE_STATE_IR
)) return;
350 /* set the new sensitivity */
351 get_ir_sens(wm
, &block1
, &block2
);
353 wiiuse_write_data(wm
, WM_REG_IR_BLOCK1
, (ubyte
*)block1
, 9,NULL
);
354 wiiuse_write_data(wm
, WM_REG_IR_BLOCK2
, (ubyte
*)block2
, 2,NULL
);
356 WIIUSE_DEBUG("Set IR sensitivity to level %i (unid %i)", level
, wm
->unid
);
361 * @brief Calculate the data from the IR spots. Basic IR mode.
363 * @param wm Pointer to a wiimote_t structure.
364 * @param data Data returned by the wiimote for the IR spots.
366 void calculate_basic_ir(struct wiimote_t
* wm
, ubyte
* data
) {
367 struct ir_dot_t
* dot
= wm
->ir
.dot
;
370 dot
[0].rx
= 1023 - (data
[0] | ((data
[2] & 0x30) << 4));
371 dot
[0].ry
= data
[1] | ((data
[2] & 0xC0) << 2);
373 dot
[1].rx
= 1023 - (data
[3] | ((data
[2] & 0x03) << 8));
374 dot
[1].ry
= data
[4] | ((data
[2] & 0x0C) << 6);
376 dot
[2].rx
= 1023 - (data
[5] | ((data
[7] & 0x30) << 4));
377 dot
[2].ry
= data
[6] | ((data
[7] & 0xC0) << 2);
379 dot
[3].rx
= 1023 - (data
[8] | ((data
[7] & 0x03) << 8));
380 dot
[3].ry
= data
[9] | ((data
[7] & 0x0C) << 6);
382 /* set each IR spot to visible if spot is in range */
383 for (i
= 0; i
< 4; ++i
) {
384 dot
[i
].rx
= BIG_ENDIAN_SHORT(dot
[i
].rx
);
385 dot
[i
].ry
= BIG_ENDIAN_SHORT(dot
[i
].ry
);
387 if (dot
[i
].ry
== 1023)
391 dot
[i
].size
= 0; /* since we don't know the size, set it as 0 */
395 interpret_ir_data(&wm
->ir
,&wm
->orient
,WIIMOTE_IS_SET(wm
, WIIMOTE_STATE_ACC
));
400 * @brief Calculate the data from the IR spots. Extended IR mode.
402 * @param wm Pointer to a wiimote_t structure.
403 * @param data Data returned by the wiimote for the IR spots.
405 void calculate_extended_ir(struct wiimote_t
* wm
, ubyte
* data
) {
406 struct ir_dot_t
* dot
= wm
->ir
.dot
;
409 for (i
= 0; i
< 4; ++i
) {
410 dot
[i
].rx
= 1023 - (data
[3*i
] | ((data
[(3*i
)+2] & 0x30) << 4));
411 dot
[i
].ry
= data
[(3*i
)+1] | ((data
[(3*i
)+2] & 0xC0) << 2);
413 dot
[i
].size
= data
[(3*i
)+2];
415 dot
[i
].rx
= BIG_ENDIAN_SHORT(dot
[i
].rx
);
416 dot
[i
].ry
= BIG_ENDIAN_SHORT(dot
[i
].ry
);
418 dot
[i
].size
= dot
[i
].size
&0x0f;
420 /* if in range set to visible */
421 if (dot
[i
].ry
== 1023)
427 interpret_ir_data(&wm
->ir
,&wm
->orient
,WIIMOTE_IS_SET(wm
, WIIMOTE_STATE_ACC
));
438 // half-height of the IR sensor if half-width is 1
439 #define HEIGHT (384.0f / 512.0f)
440 // maximum sensor bar slope (tan(35 degrees))
441 #define MAX_SB_SLOPE 0.7f
442 // minimum sensor bar width in view, relative to half of the IR sensor area
443 #define MIN_SB_WIDTH 0.1f
444 // reject "sensor bars" that happen to have a dot towards the middle
445 #define SB_MIDDOT_REJECT 0.05f
447 // physical dimensions
448 // cm center to center of emitters
449 #define SB_WIDTH 19.5f
450 // half-width in cm of emitters
451 #define SB_DOT_WIDTH 2.25f
452 // half-height in cm of emitters (with some tolerance)
453 #define SB_DOT_HEIGHT 1.0f
455 #define SB_DOT_WIDTH_RATIO (SB_DOT_WIDTH / SB_WIDTH)
456 #define SB_DOT_HEIGHT_RATIO (SB_DOT_HEIGHT / SB_WIDTH)
458 // dots further out than these coords are allowed to not be picked up
459 // otherwise assume something's wrong
460 //#define SB_OFF_SCREEN_X 0.8f
461 //#define SB_OFF_SCREEN_Y (0.8f * HEIGHT)
463 // disable, may be doing more harm than good due to sensor pickup glitches
464 #define SB_OFF_SCREEN_X 0.0f
465 #define SB_OFF_SCREEN_Y 0.0f
467 // if a point is closer than this to one of the previous SB points
468 // when it reappears, consider it the same instead of trying to guess
469 // which one of the two it is
470 #define SB_SINGLE_NOGUESS_DISTANCE (100.0 * 100.0)
472 // width of the sensor bar in pixels at one meter from the Wiimote
473 #define SB_Z_COEFFICIENT 256.0f
475 // distance in meters from the center of the FOV to the left or right edge,
476 // when the wiimote is at one meter
477 #define WIIMOTE_FOV_COEFFICIENT 0.39f
479 #define SQUARED(x) ((x)*(x))
480 #define WMAX(x,y) ((x>y)?(x):(y))
481 #define WMIN(x,y) ((x<y)?(x):(y))
484 * @brief Interpret IR data into more user friendly variables.
486 * @param wm Pointer to a wiimote_t structure.
488 void find_sensorbar(struct ir_t
* ir
, struct orient_t
*orient
) {
489 struct fdot_t dots
[4];
490 struct fdot_t acc_dots
[4];
492 struct sb_t candidates
[6];
497 int num_candidates
= 0;
503 WIIUSE_DEBUG("IR: orient angle: %.02f\n",orient
->roll
);
505 /* count visible dots and populate dots structure */
506 /* dots[] is in -1..1 units for width */
508 for (i
= 0; i
< 4; i
++) {
509 if (ir
->dot
[i
].visible
) {
510 dots
[ir
->num_dots
].x
= (ir
->dot
[i
].rx
- 512.0f
) / 512.0f
;
511 dots
[ir
->num_dots
].y
= (ir
->dot
[i
].ry
- 384.0f
) / 512.0f
;
512 WIIUSE_DEBUG("IR: dot %d at (%d,%d) (%.03f,%.03f)\n",ir
->num_dots
,ir
->dot
[i
].rx
,ir
->dot
[i
].ry
,dots
[ir
->num_dots
].x
,dots
[ir
->num_dots
].y
);
517 WIIUSE_DEBUG("IR: found %d dots\n",ir
->num_dots
);
520 if(ir
->num_dots
== 0) {
521 if(ir
->state
!= IR_STATE_DEAD
)
522 ir
->state
= IR_STATE_LOST
;
530 /* ==== Find the Sensor Bar ==== */
532 // first rotate according to accelerometer orientation
533 rotate_dots(dots
, acc_dots
, ir
->num_dots
, orient
->roll
);
534 if(ir
->num_dots
> 1) {
535 WIIUSE_DEBUG("IR: locating sensor bar candidates\n");
537 // iterate through all dot pairs
538 for(first
=0; first
< (ir
->num_dots
-1); first
++) {
539 for(second
=(first
+1); second
< ir
->num_dots
; second
++) {
540 WIIUSE_DEBUG("IR: trying dots %d and %d\n",first
,second
);
541 // order the dots leftmost first into cand
542 // storing both the raw dots and the accel-rotated dots
543 if(acc_dots
[first
].x
> acc_dots
[second
].x
) {
544 cand
.dots
[0] = dots
[second
];
545 cand
.dots
[1] = dots
[first
];
546 cand
.acc_dots
[0] = acc_dots
[second
];
547 cand
.acc_dots
[1] = acc_dots
[first
];
549 cand
.dots
[0] = dots
[first
];
550 cand
.dots
[1] = dots
[second
];
551 cand
.acc_dots
[0] = acc_dots
[first
];
552 cand
.acc_dots
[1] = acc_dots
[second
];
554 difference
.x
= cand
.acc_dots
[1].x
- cand
.acc_dots
[0].x
;
555 difference
.y
= cand
.acc_dots
[1].y
- cand
.acc_dots
[0].y
;
558 if(fabsf(difference
.y
/ difference
.x
) > MAX_SB_SLOPE
)
560 WIIUSE_DEBUG("IR: passed angle check\n");
561 // rotate to the true sensor bar angle
562 cand
.off_angle
= -RAD_TO_DEGREE(atan2(difference
.y
, difference
.x
));
563 cand
.angle
= cand
.off_angle
+ orient
->roll
;
564 rotate_dots(cand
.dots
, cand
.rot_dots
, 2, cand
.angle
);
565 WIIUSE_DEBUG("IR: off_angle: %.02f, angle: %.02f\n", cand
.off_angle
, cand
.angle
);
566 // recalculate x distance - y should be zero now, so ignore it
567 difference
.x
= cand
.rot_dots
[1].x
- cand
.rot_dots
[0].x
;
570 if(difference
.x
< MIN_SB_WIDTH
)
572 // middle dot check. If there's another source somewhere in the
573 // middle of this candidate, then this can't be a sensor bar
575 for(i
=0; i
<ir
->num_dots
; i
++) {
578 if(i
==first
|| i
==second
) continue;
579 hadj
= SB_DOT_HEIGHT_RATIO
* difference
.x
;
580 wadj
= SB_DOT_WIDTH_RATIO
* difference
.x
;
581 rotate_dots(&dots
[i
], &tdot
, 1, cand
.angle
);
582 if( ((cand
.rot_dots
[0].x
+ wadj
) < tdot
.x
) &&
583 ((cand
.rot_dots
[1].x
- wadj
) > tdot
.x
) &&
584 ((cand
.rot_dots
[0].y
+ hadj
) > tdot
.y
) &&
585 ((cand
.rot_dots
[0].y
- hadj
) < tdot
.y
))
588 // failed middle dot check
589 if(i
< ir
->num_dots
) continue;
590 WIIUSE_DEBUG("IR: passed middle dot check\n");
592 cand
.score
= 1 / (cand
.rot_dots
[1].x
- cand
.rot_dots
[0].x
);
594 // we have a candidate, store it
595 WIIUSE_DEBUG("IR: new candidate %d\n",num_candidates
);
596 candidates
[num_candidates
++] = cand
;
601 if(num_candidates
== 0) {
608 // no sensor bar candidates, try to work with a lone dot
609 WIIUSE_DEBUG("IR: no candidates\n");
612 WIIUSE_DEBUG("IR: we're dead\n");
613 // we've never seen a sensor bar before, so we're screwed
620 case IR_STATE_SINGLE
:
622 WIIUSE_DEBUG("IR: trying to keep track of single dot\n");
623 // try to find the dot closest to the previous sensor bar position
624 for(i
=0; i
<ir
->num_dots
; i
++) {
625 WIIUSE_DEBUG("IR: checking dot %d (%.02f, %.02f)\n",i
, acc_dots
[i
].x
,acc_dots
[i
].y
);
627 WIIUSE_DEBUG(" to dot %d (%.02f, %.02f)\n",j
, ir
->sensorbar
.acc_dots
[j
].x
,ir
->sensorbar
.acc_dots
[j
].y
);
628 d
= SQUARED(acc_dots
[i
].x
- ir
->sensorbar
.acc_dots
[j
].x
);
629 d
+= SQUARED(acc_dots
[i
].y
- ir
->sensorbar
.acc_dots
[j
].y
);
637 WIIUSE_DEBUG("IR: closest dot is %d to %d\n",closest
,closest_to
);
638 if(ir
->state
!= IR_STATE_LOST
|| best
< SB_SINGLE_NOGUESS_DISTANCE
) {
639 // now work out where the other dot would be, in the acc frame
640 sb
.acc_dots
[closest_to
] = acc_dots
[closest
];
641 sb
.acc_dots
[closest_to
^1].x
= ir
->sensorbar
.acc_dots
[closest_to
^1].x
- ir
->sensorbar
.acc_dots
[closest_to
].x
+ acc_dots
[closest
].x
;
642 sb
.acc_dots
[closest_to
^1].y
= ir
->sensorbar
.acc_dots
[closest_to
^1].y
- ir
->sensorbar
.acc_dots
[closest_to
].y
+ acc_dots
[closest
].y
;
644 rotate_dots(sb
.acc_dots
, sb
.dots
, 2, -orient
->roll
);
645 if((fabsf(sb
.dots
[closest_to
^1].x
) < SB_OFF_SCREEN_X
) && (fabsf(sb
.dots
[closest_to
^1].y
) < SB_OFF_SCREEN_Y
)) {
646 // this dot should be visible but isn't, since the candidate section failed.
647 // fall through and try to pick out the sensor bar without previous information
648 WIIUSE_DEBUG("IR: dot falls on screen, falling through\n");
650 // calculate the rotated dots frame
651 // angle tends to drift, so recalculate
652 sb
.off_angle
= -RAD_TO_DEGREE(atan2(sb
.acc_dots
[1].y
- sb
.acc_dots
[0].y
, sb
.acc_dots
[1].x
- sb
.acc_dots
[0].x
));
653 sb
.angle
= ir
->sensorbar
.off_angle
+ orient
->roll
;
654 rotate_dots(sb
.acc_dots
, sb
.rot_dots
, 2, ir
->sensorbar
.off_angle
);
655 WIIUSE_DEBUG("IR: kept track of single dot\n");
659 WIIUSE_DEBUG("IR: lost the dot and new one is too far away\n");
661 // try to find the dot closest to the sensor edge
662 WIIUSE_DEBUG("IR: trying to find best dot\n");
663 for(i
=0; i
<ir
->num_dots
; i
++) {
664 d
= WMIN(1.0f
- fabsf(dots
[i
].x
), HEIGHT
- fabsf(dots
[i
].y
));
670 WIIUSE_DEBUG("IR: best dot: %d\n",closest
);
671 // now try it as both places in the sensor bar
672 // and pick the one that places the other dot furthest off-screen
674 sbx
[i
].acc_dots
[i
] = acc_dots
[closest
];
675 sbx
[i
].acc_dots
[i
^1].x
= ir
->sensorbar
.acc_dots
[i
^1].x
- ir
->sensorbar
.acc_dots
[i
].x
+ acc_dots
[closest
].x
;
676 sbx
[i
].acc_dots
[i
^1].y
= ir
->sensorbar
.acc_dots
[i
^1].y
- ir
->sensorbar
.acc_dots
[i
].y
+ acc_dots
[closest
].y
;
677 rotate_dots(sbx
[i
].acc_dots
, sbx
[i
].dots
, 2, -orient
->roll
);
678 dx
[i
] = WMAX(fabsf(sbx
[i
].dots
[i
^1].x
),fabsf(sbx
[i
].dots
[i
^1].y
/ HEIGHT
));
681 WIIUSE_DEBUG("IR: dot is LEFT: %.02f > %.02f\n",dx
[0],dx
[1]);
684 WIIUSE_DEBUG("IR: dot is RIGHT: %.02f < %.02f\n",dx
[0],dx
[1]);
687 // angle tends to drift, so recalculate
688 sb
.off_angle
= -RAD_TO_DEGREE(atan2(sb
.acc_dots
[1].y
- sb
.acc_dots
[0].y
, sb
.acc_dots
[1].x
- sb
.acc_dots
[0].x
));
689 sb
.angle
= ir
->sensorbar
.off_angle
+ orient
->roll
;
690 rotate_dots(sb
.acc_dots
, sb
.rot_dots
, 2, ir
->sensorbar
.off_angle
);
691 WIIUSE_DEBUG("IR: found new dot to track\n");
695 ir
->state
= IR_STATE_SINGLE
;
699 WIIUSE_DEBUG("IR: finding best candidate\n");
700 // look for the best candidate
701 // for now, the formula is simple: pick the one with the smallest distance
702 for(i
=0; i
<num_candidates
; i
++) {
703 if(candidates
[i
].score
> best
) {
705 best
= candidates
[i
].score
;
708 WIIUSE_DEBUG("IR: best candidate: %d\n",bestidx
);
709 sb
= candidates
[bestidx
];
710 ir
->state
= IR_STATE_GOOD
;
714 ir
->ax
= ((sb
.rot_dots
[0].x
+ sb
.rot_dots
[1].x
) / 2) * 512.0 + 512.0;
715 ir
->ay
= ((sb
.rot_dots
[0].y
+ sb
.rot_dots
[1].y
) / 2) * 512.0 + 384.0;
717 ir
->distance
= (sb
.rot_dots
[1].x
- sb
.rot_dots
[0].x
) * 512.0;
721 #define SMOOTH_IR_RADIUS 8.0f
722 #define SMOOTH_IR_SPEED 0.25f
723 #define SMOOTH_IR_DEADZONE 2.5f
726 * @brief Smooth the IR pointer position
728 * @param ir Pointer to an ir_t structure.
730 void apply_ir_smoothing(struct ir_t
*ir
) {
731 f32 dx
, dy
, d
, theta
;
733 WIIUSE_DEBUG("Smooth: OK (%.02f, %.02f) LAST (%.02f, %.02f) ", ir
->ax
, ir
->ay
, ir
->sx
, ir
->sy
);
734 dx
= ir
->ax
- ir
->sx
;
735 dy
= ir
->ay
- ir
->sy
;
736 d
= sqrtf(dx
*dx
+ dy
*dy
);
737 if (d
> SMOOTH_IR_DEADZONE
) {
738 if (d
< SMOOTH_IR_RADIUS
) {
739 WIIUSE_DEBUG("INSIDE\n");
740 ir
->sx
+= dx
* SMOOTH_IR_SPEED
;
741 ir
->sy
+= dy
* SMOOTH_IR_SPEED
;
743 WIIUSE_DEBUG("OUTSIDE\n");
744 theta
= atan2f(dy
, dx
);
745 ir
->sx
= ir
->ax
- cosf(theta
) * SMOOTH_IR_RADIUS
;
746 ir
->sy
= ir
->ay
- sinf(theta
) * SMOOTH_IR_RADIUS
;
749 WIIUSE_DEBUG("DEADZONE\n");
753 // max number of errors before cooked data drops out
754 #define ERROR_MAX_COUNT 8
755 // max number of glitches before cooked data updates
756 #define GLITCH_MAX_COUNT 5
757 // squared delta over which we consider something a glitch
758 #define GLITCH_DIST (150.0f * 150.0f)
761 * @brief Interpret IR data into more user friendly variables.
763 * @param ir Pointer to an ir_t structure.
764 * @param orient Pointer to an orient_t structure.
766 void interpret_ir_data(struct ir_t
* ir
, struct orient_t
*orient
) {
771 find_sensorbar(ir
, orient
);
774 ir
->angle
= ir
->sensorbar
.angle
;
775 ir
->z
= SB_Z_COEFFICIENT
/ ir
->distance
;
776 orient
->yaw
= calc_yaw(ir
);
777 if(ir
->error_cnt
>= ERROR_MAX_COUNT
) {
782 d
= SQUARED(ir
->ax
- ir
->sx
) + SQUARED(ir
->ay
- ir
->sy
);
783 if(d
> GLITCH_DIST
) {
784 if(ir
->glitch_cnt
> GLITCH_MAX_COUNT
) {
785 apply_ir_smoothing(ir
);
792 apply_ir_smoothing(ir
);
795 ir
->smooth_valid
= 1;
798 if(ir
->error_cnt
>= ERROR_MAX_COUNT
) {
799 ir
->smooth_valid
= 0;
801 ir
->smooth_valid
= 1;
805 if(ir
->smooth_valid
) {
808 if (ir_correct_for_bounds(&x
, &y
, ir
->aspect
, ir
->offset
[0], ir
->offset
[1])) {
809 ir_convert_to_vres(&x
, &y
, ir
->aspect
, ir
->vres
[0], ir
->vres
[1]);
822 * @brief Calculate yaw given the IR data.
824 * @param ir IR data structure.
826 float calc_yaw(struct ir_t
* ir
) {
830 x
*= WIIMOTE_FOV_COEFFICIENT
/ 512.0;
832 return RAD_TO_DEGREE( atanf(x
) );