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