Welcome! Log In Create A New Profile

Advanced

Nunchuck accuracy (reading centre problem)

Posted by Titmouse 
Nunchuck accuracy (reading centre problem)
January 21, 2012 05:48PM
I've found the values from nunchuk.js.center (expansion_t structure) give wild x/y results, it will work fine for one nunchuck yet not for another. Both my nunchucks are Nintendo and both work fine for official games.

I've fixed this with the second example - both of my nunchucks work fine for wiibrew now.
I'm going to avoid using the expansion_t structure's centre values.
The second example keeps up with the min and max refreshing/changing (which they do!!!), I've not yet seen the centre values refresh.
The resting accuracy is also far better - so no need for large dead zones since it should never drift with time.

WPADData* pPadData(WPAD_Data(PadCount));	// top level structure
expansion_t* data = &pPadData->exp;			// expansion devices like nunchuck

switch (data->type)
{
	case WPAD_EXP_NUNCHUK:
	// this seems to be the standard method for home brew
	// will give bad results as centre x/y may have bad values 
	//given at init time, so have no relation to the working data!!!
	float mx= (10.0* (data->nunchuk.js.pos.x - data->nunchuk.js.center.x))/data->nunchuk.js.max.x;
	float my= (10.0* (data->nunchuk.js.pos.y - data->nunchuk.js.center.y))/data->nunchuk.js.max.y;
	printf("\x1b[9;25HNCUHCK mx %1.3f my %1.3f  ", mx, my);
	// This gives far better results 
	int CentreX= data->nunchuk.js.min.x + ((data->nunchuk.js.max.x -  data->nunchuk.js.min.x)/2);
	int CentreY= data->nunchuk.js.min.y + ((data->nunchuk.js.max.y -  data->nunchuk.js.min.y)/2);
	mx = (10.0 * (data->nunchuk.js.pos.x - CentreX )) / data->nunchuk.js.max.x;
	my = (10.0 * (data->nunchuk.js.pos.y - CentreY )) / data->nunchuk.js.max.y;
	printf("\x1b[10;25HNCUHCK mx %1.3f my %1.3f  ", mx, my);
Re: Nunchuck accuracy (reading centre problem)
January 22, 2012 03:22AM
Such a funny coincidence that I was working on the very same problem all day today! (wasted the whole day).

Now here is my version. This will generate a float value between -1,0 and 1 (Because I just love working with polar numbers). Most times it doesn't reach absolute 1 or -1 but it doesn't really matter as long as you are using the value in a analog fashion (e.g speed * val). here is a section;

	float analog_margin=0.1;
        float button_analog_x_left=0.0f, button_analog_y_left=0.0f;  //left stick
	float button_analog_x_right=0.0f, button_analog_y_right=0.0f;  //right stick
	
	float button_analog_a_left=0.0f, button_analog_a_right=0.0f;  //angle of stick, only for wii sticks
	
	button_analog_x_left=0.0f; button_analog_y_left=0.0f;
	button_analog_x_right=0.0f; button_analog_y_right=0.0f;

	if (data.type == WPAD_EXP_NUNCHUK) {
	 if( (button_analog_x_left==0) && (button_analog_y_left==0) ) { //only check of not changed
		button_analog_x_left=(data.nunchuk.js.pos.x - data.nunchuk.js.center.x )/100.0f;
		button_analog_y_left=(data.nunchuk.js.pos.y - data.nunchuk.js.center.y )/100.0f;							
		button_analog_x_left=zero_if_between( button_analog_x_left, -analog_margin, analog_margin );
		button_analog_y_left=zero_if_between( button_analog_y_left, -analog_margin, analog_margin );
	 }
        }
	if (data.type == WPAD_EXP_CLASSIC) {
			if( (button_analog_x_left==0) && (button_analog_y_left==0) ) { //only check of not changed
				button_analog_x_left=( (data.classic.ljs.pos.x - data.classic.ljs.center.x)/ (float)data.classic.ljs.max.x )*2.0f ;  //sticks 50% precision 
				button_analog_y_left=( (data.classic.ljs.pos.y - data.classic.ljs.center.y)/ (float)data.classic.ljs.max.y )*2.0f ;	
				
				button_analog_x_left=zero_if_between( button_analog_x_left, -analog_margin, analog_margin );
				button_analog_y_left=zero_if_between( button_analog_y_left, -analog_margin, analog_margin );
				
				button_analog_a_left=data.classic.ljs.ang;
			}
			
			if( (button_analog_x_right==0) && (button_analog_y_right==0) ) { //only check of not changed
				button_analog_x_right=( (data.classic.rjs.pos.x - data.classic.rjs.center.x)/ (float)data.classic.rjs.max.x )*2.0f ;
				button_analog_y_right=( (data.classic.rjs.pos.y - data.classic.rjs.center.y)/ (float)data.classic.rjs.max.y )*2.0f ;	
				
				button_analog_x_right=zero_if_between( button_analog_x_right, -analog_margin, analog_margin );
				button_analog_y_right=zero_if_between( button_analog_y_right, -analog_margin, analog_margin );
				
				button_analog_a_right=data.classic.ljs.ang;
			}
			
		}
		//game cube controller - do this last because its near always active
		if( PAD_ScanPads() ) {
			if( (button_analog_x_left==0) && (button_analog_y_left==0) ) { //only check of not changed
				button_analog_x_left=PAD_StickX(0)/105.0f;
				button_analog_y_left=PAD_StickY(0)/105.0f;	
				
				button_analog_x_left=zero_if_between( button_analog_x_left, -analog_margin, analog_margin );
				button_analog_y_left=zero_if_between( button_analog_y_left, -analog_margin, analog_margin );
			}
			
			if( (button_analog_x_right==0) && (button_analog_y_right==0) ) { //only check of not changed
				button_analog_x_right=PAD_SubStickX(0)/105.0f;
				button_analog_y_right=PAD_SubStickY(0)/105.0f;	
				
				button_analog_x_right=zero_if_between( button_analog_x_right, -analog_margin, analog_margin );
				button_analog_y_right=zero_if_between( button_analog_y_right, -analog_margin, analog_margin );
			}	
		}

		//analog stick to button translation (remove this if you use your arrow buttons for other things)
		if (button_analog_y_left > analog_margin ) button_up_held=true; //left stick
		if (button_analog_y_left < -analog_margin ) button_down_held=true;
		if (button_analog_x_left > analog_margin ) button_right_held=true; 
		if (button_analog_x_left < -analog_margin ) button_left_held=true;

		if (button_analog_y_right > analog_margin ) button_up_scroll=true;//right stick
		if (button_analog_y_right < -analog_margin ) button_down_scroll=true;
		if (button_analog_x_right > analog_margin ) button_right_scroll=true;
		if (button_analog_x_right < -analog_margin ) button_left_scroll=true;

other unnecessary stuff
float zero_if_between(float numf, float low, float high) {
	if ( (numf < high) & (numf > low) ) return 0.0f;
	return numf;
}
Re: Nunchuck accuracy (reading centre problem)
January 22, 2012 03:54AM
Issue with the first example: the centre position often isn't always in the middle of the min+(max-min)/2 range. For example the min reported value might be 3, the max value might be 120 and the stick returns 64 when idle. This would get interpreted as a positive value instead of centered.

Issue with the second example: not all classic controllers contain calibration data so sometimes the center.x/y values are completely wrong, for example they will be zero but the controller actually returns {-4,-2} when centered. This is especially noticeable for the right stick which has small precision [-32:31].

Solution to both: either grab the stick values over several milliseconds when the extension is first connected and use them as the center values (this is what most real games do) or add a controller calibration option that the user can access to gather the various values e.g:
- tell the user to push the stick to top-right and press a button (record max values)
- tell the user to push the stick to bottom-left and press a button (record max values)
- tell the user to let go of the stick and press a button (record centered values)
Re: Nunchuck accuracy (reading centre problem)
January 22, 2012 11:01AM
Second part of the first post does use dynamic min & max? Just as long as a dead zone is given allowing for all types of nunchucks.

This is the method I'm using - but I've only been look at nunchuck coding for a day - so this is my first attempt, any issues let me know
(it's self configuring and gives -1 to 1 values that I find useful)


static float offsetX(0);
if ( (fabs(mx) - 100.0f) > offsetX)
{
	if (mx<0)
		offsetX = (-mx) - 100.0f;
	else
		offsetX = 100.0f - mx;
}
static float offsetY(0);
if ( (fabs(my) - 100.0f) > offsetY)
{
	if (my<0)
		offsetX = (-my) - 100.0f;
	else
		offsetX = 100.0f - my;
}

// just incase a new nunchuck is swapped in (new levels need setting)
if (fabs(mx+offsetX) > 100.0f)
	offsetX = 0;  // reset since swapping in a new nunchuck will need re-offsetting 
if (fabs(my+offsetY) > 100.0f)
	offsetY = 0;	// again... needs re-offsetting 

mx+=offsetX;
my+=offsetY;
mx*=0.01;
my*=0.01;
printf("\x1b[11;25HNCUHCK mx %1.3f my %1.3f  ", mx, my);



Edited 2 time(s). Last edit at 01/22/2012 11:07AM by Titmouse.
Re: Nunchuck accuracy (reading centre problem)
January 22, 2012 01:30PM
@tueidj It would be like test for a person playing with a broken n64 joystick its either not going to work or the person would have to adjust himself to accomodate. but I see what you are saying, right now I can only test the optimum situation (standard classic controller, nunchuk) and it does fall back to the d-pad if all else fails.

@Titmouse yeah the person keeps swapping out continuosly worst nunchucks as they plays, lol.
Re: Nunchuck accuracy (reading centre problem)
January 23, 2012 03:38PM
Owen, best avoid using the center.x/y values, please consider calculating them as in my first example - I would never use these center values myself.

Please note: One of my 2 nunchucks (mine are all identical & nintendo make) does not work with any wii brew but works just fine with normal games I don't think I've found a single wii brew app that it works with!!!, yet my code does (in both examples) so its not a hard to do or support just like the big boys.

Swapping nunchuck - Consider things happen like batteries run out, anything could happen after that. We must allow for it.
Re: Nunchuck accuracy (reading centre problem)
January 23, 2012 05:29PM
I personally haven't used the nunchuk much but your code doesn't look hard to implement.
Re: Nunchuck accuracy (reading centre problem)
January 23, 2012 06:40PM
I'm probably going to use this for my stuff (sorry lost 1/2 in the original cut'n'paste in my above post)
It's my test routine, so has fudged static floats which need replacing if you need to use more wiimotes.
Becomes more accurate over time (few seconds of play!) but will still work very well before that point, it's just finding the optimum center for the -/+ levels.

WPADData* pPadData(WPAD_Data( 0 ));	// top level structure, hard coded zero for example)
expansion_t* pData = &pPadData->exp;

float rX = pData->nunchuk.js.pos.x - pData->nunchuk.js.min.x;
float rY = pData->nunchuk.js.pos.y - pData->nunchuk.js.min.y;
rX -= (pData->nunchuk.js.max.x - pData->nunchuk.js.min.x) / 2;
rY -= (pData->nunchuk.js.max.y - pData->nunchuk.js.min.y) / 2;
//------------------------------------------------------------------------------------
static float DriftX(0);
if ( (fabs(rX) - 100.0f) > DriftX)
{
	if (rX<0)
		DriftX = (-rX) - 100.0f;
	else
		DriftX = 100.0f - rX;
}
//------------------------------------------------------------------------------------
static float DriftY(0);
if ( (fabs(rY) - 100.0f) > DriftY)
{
	if (rY<0)
		DriftY = (-rY) - 100.0f;
	else
		DriftY = 100.0f - rY;
}
//------------------------------------------------------------------------------------
// just incase a new nunchuck is swapped in (new levels need setting)
// should only happen when new h/w is swapped in during play
if (fabs(rX + DriftX) > 100.0f)
	DriftX = 0;  // reset since swapping in a new nunchuck will need re-offsetting 
if (fabs(rY + DriftY) > 100.0f)
	DriftY = 0;	// again... needs re-offsetting 
//------------------------------------------------------------------------------------
rX += DriftX;	// Corrects distance between opposite directions
rY += DriftX;
rX *= 0.01;		// scale -1 to +1
rY *= 0.01;

if (fabs(rX) < 0.15f) rX = 0;    // dead zone - not sure what's a good value yet (this may be too small)
if (fabs(rY) < 0.15f) rY = 0;

printf("\x1b[11;25HNCUHCK mx %1.4f my %1.4f  ", rX, rY);



Edited 1 time(s). Last edit at 01/24/2012 01:41PM by Titmouse.
Re: Nunchuck accuracy (reading centre problem)
July 17, 2012 05:49AM
It works like a charm! Thanks! :)
Sorry, only registered users may post in this forum.

Click here to login