Joystick calibration in GNU/Linux

Note: joystick correction parameters can now be set in the GIMX configurations, so there’s no need anymore to do any calibration at the operating system level.

The reference tool to tweak joysticks from userland in GNU/Linux is jscal. It is a command line tool that can be used to calibrate or remap joystick controls. Its calibration mode (jscal -c /dev/input/jsX) sadly does not work for calibrating the pedals from my Logitech Momo Racing wheel. Surprisingly, the manual is very vague about how the correction works:

 -s, --set-correction <nb_axes,type,precision,coefficients,...>
Sets correction to specified values. For each axis, specify the
correction type (0 for none, 1 for "broken line"), the precision,
and if necessary the correction coefficients ("broken line" 
corrections take four coefficients).
-p, --print-correction
Prints the current correction settings. The format of the output
is a jscal command line.

What’s a “broken-line” correction? What are the coefficients used for? I only found the answer in the kernel source code:
static int joydev_correct(int value, struct js_corr *corr)
{
  switch (corr->type) {
  case JS_CORR_NONE:
    break;

  case JS_CORR_BROKEN:
    value = value > corr->coef[0] ? (value < corr->coef[1] ? 0 : 
            ((corr->coef[3] * (value - corr->coef[1])) >> 14)) : 
            ((corr->coef[2] * (value - corr->coef[0])) >> 14);
    break;

  default:
    return 0;
  }

  return value < -32767 ? -32767 : (value > 32767 ? 32767 : value);
}

The value calculation can be expanded like this:
if (value > corr->coef[0])
{
  if (value < corr->coef[1])
  {
    value = 0;
  }
  else
  {
    value = corr->coef[3] * (value - corr->coef[1])) >> 14;
  }
}
else
{
  value = corr->coef[2] * (value - corr->coef[0])) >> 14;
}

This leads to the following deductions:

  • coef[0] and coef[1] are two axis values
  • coef[2] and coef[3] are the correction factors that apply respectively below and above these axis values
  • coef[0] < coef[1] means there is a dead zone between these two axis values
  • coef[1] = coef[0] means there is no dead zone
  • coef[0] > coef[1] means coef[1] is ignored: it behaves as if coef[1] = coef[0]
  • the resulting value is divided by 16384

Let’s see what are the default correction settings for a Logitech Momo Racing wheel:

matlo@matlo-desktop ~ $ jscal -p /dev/input/js1
jscal -s 3,1,0,511,511,1050628,1050628,1,0,127,127,4227330,4227330,1,0,127,127,4227330,4227330 /dev/input/js1

Both gas and brake pedals have the same correction: 1,0,127,127,4227330,4227330
This maps a value from the [0,255] range to the [-32767,32767] range. The rest value 255 is mapped to 32767 when I expect it to be mapped to 0, and the “fully pressed” value 0 is mapped to -32767 when I expect it to be mapped to 32767…
To fix this, it’s possible to set coef[0] to 255 (which means coef[1] and coef[3] are not used and can be set to 0), and to set coef[2] to 32767 x 16384 / -255 = -2105312. The resulting jscal command is:
matlo@matlo-desktop ~ $ jscal -s 3,1,0,511,511,1050628,1050628,1,0,255,0,-2105312,0,1,0,255,0,-2105312,0 /dev/input/js1

Finally, it’s possible to save the calibration values:
matlo@matlo-desktop ~ $ sudo jscal-store /dev/input/js1
matlo@matlo-desktop ~ $ cat /var/lib/joystick/joystick.state 
NAME="Logitech  Logitech MOMO Racing "
VENDOR="046d"
PRODUCT="ca03"
jscal -u 3,0,1,2,10,288,289,290,291,292,293,294,295,296,297
jscal -s 3,1,0,511,511,1050628,1050628,1,0,255,0,-2105312,0,1,0,255,0,-2105312,0

3 Replies to “Joystick calibration in GNU/Linux”

  1. I tried calibrate ps3 move controller with no sucess using the GUI, jstest-gtk, it will be awesome move on ps4, now im trying using an API for move(moveonpc).

      1. Ps4 dont support ps move on games till now, even ps3… i would like play some FPS games that dont support move, with move and ps camera.

Leave a Reply to Cybereu Cancel reply

Your email address will not be published. Required fields are marked *