ds4drv
ds4drv copied to clipboard
Configurable calibration
Analog controllers have inherit incorrectness that require calibration. This varies from device to device, so it should be user configurable. Calibration includes setting the minimum, maximum, center and deadzone values for axes.
Currently calibration parameters can be modified on the Python code (axes_options on uinput.py), but not with the configuration file. I also believe that this calibration should not be done per mapping but per device (or per configuration file since most users only have one controller). The calibration should be applied before any further mappings or other input logic is processed.
In addition the default deadzone for axes should be lowered from 15 to 5 (or even as low as 3 or 4). DualShock 4 is a really good controller in terms of deadzones, so we should not waste that quality.
I would agree on the smaller default dead-zones.
Yeah, I agree, I simply chose the 15 deadzone as default since that is what is used on the regular joystick device that is created by the Linux kernel. But I'm not sure if this is what is default in Linux or is actually requested by the device.
I'm not sure why, but I have 5 DualShock 4 controllers (yes, five), and all of them seem to emit ever-changing left/right_analog_x/y values around the center value (like 120~130), resulting in a lot of events unnecessarily. To work around this problem, I had to apply some dead zone to left/right_analog_x/y values like the following:
diff --git a/ds4drv/uinput.py b/ds4drv/uinput.py
index 062c231..13ce21f 100644
--- a/ds4drv/uinput.py
+++ b/ds4drv/uinput.py
@@ -8,6 +8,8 @@ from evdev import util
from .exceptions import DeviceError
+from math import sqrt
+
# Check for the existence of a "resolve_ecodes_dict" function.
# Need to know if axis options tuples should be altered.
# This is needed to keep the code compatible with python-evdev < 0.6.0.
@@ -303,6 +305,8 @@ class UInputDevice(object):
def write_event(self, etype, code, value):
"""Writes a event to the device, if it has changed."""
+ if code < 0:
+ return
last_value = self._write_cache.get(code)
if last_value != value:
self.device.write(etype, code, value)
@@ -311,10 +315,47 @@ class UInputDevice(object):
def emit(self, report):
"""Writes axes, buttons and hats with values from the report to
the device."""
+ left_analog_x_code = -1
+ left_analog_x = 127
+ left_analog_y_code = -1
+ left_analog_y = 127
+ right_analog_x_code = -1
+ right_analog_x = 127
+ right_analog_y_code = -1
+ right_analog_y = 127
for name, attr in self.layout.axes.items():
value = getattr(report, attr)
+ if attr == 'left_analog_x':
+ left_analog_x_code = name
+ left_analog_x = value
+ continue
+ if attr == 'left_analog_y':
+ left_analog_y_code = name
+ left_analog_y = value
+ continue
+ if attr == 'right_analog_x':
+ right_analog_x_code = name
+ right_analog_x = value
+ continue
+ if attr == 'right_analog_y':
+ right_analog_y_code = name
+ right_analog_y = value
+ continue
self.write_event(ecodes.EV_ABS, name, value)
+ # Apply 14% dead zone on analog sticks
+ if sqrt((left_analog_x - 127) ** 2 + (left_analog_y - 127) ** 2) / 128 < 0.14:
+ left_analog_x = 127
+ left_analog_y = 127
+ if sqrt((right_analog_x - 127) ** 2 + (right_analog_y - 127) ** 2) / 128 < 0.14:
+ right_analog_x = 127
+ right_analog_y = 127
+
+ self.write_event(ecodes.EV_ABS, left_analog_x_code, left_analog_x)
+ self.write_event(ecodes.EV_ABS, left_analog_y_code, left_analog_y)
+ self.write_event(ecodes.EV_ABS, right_analog_x_code, right_analog_x)
+ self.write_event(ecodes.EV_ABS, right_analog_y_code, right_analog_y)
+
for name, attr in self.layout.buttons.items():
attr, modifier = attr
The value 14% seems to work pretty well in my case. I'm not sure if this is a good place to implement dead zones though. If anybody can guide me where to implement this, then I may invest some more time to send a PR for this.