Deadzone and anti-deadzone handling (the first rugged version, not perfect yet) and small performance improvements here and there.

This commit is contained in:
mika-n 2019-02-19 11:10:16 +02:00
parent 13607ebc7c
commit 27dbab06c5
5 changed files with 102 additions and 29 deletions

View File

@ -1650,8 +1650,10 @@ namespace DS4Windows
} }
} }
if (getSASteeringWheelEmulationAxis(device) != SASteeringWheelEmulationAxisType.None) if (GetSASteeringWheelEmulationAxis(device) != SASteeringWheelEmulationAxisType.None)
{
MappedState.SASteeringWheelEmulationUnit = Mapping.Scale360degreeGyroAxis(device, eState, ctrl); MappedState.SASteeringWheelEmulationUnit = Mapping.Scale360degreeGyroAxis(device, eState, ctrl);
}
calculateFinalMouseMovement(ref tempMouseDeltaX, ref tempMouseDeltaY, calculateFinalMouseMovement(ref tempMouseDeltaX, ref tempMouseDeltaY,
out mouseDeltaX, out mouseDeltaY); out mouseDeltaX, out mouseDeltaY);
@ -3915,6 +3917,8 @@ namespace DS4Windows
gyroAccelX = exposedState.getAccelX(); gyroAccelX = exposedState.getAccelX();
gyroAccelZ = exposedState.getAccelZ(); gyroAccelZ = exposedState.getAccelZ();
//gyroAccelX = exposedState.OutputAccelX;
//gyroAccelZ = exposedState.OutputAccelZ;
// State 0=Normal mode (ie. calibration process is not running), 1=Activating calibration, 2=Calibration process running, 3=Completing calibration, 4=Cancelling calibration // State 0=Normal mode (ie. calibration process is not running), 1=Activating calibration, 2=Calibration process running, 3=Completing calibration, 4=Cancelling calibration
if (controller.WheelRecalibrateActiveState == 1) if (controller.WheelRecalibrateActiveState == 1)
@ -4057,6 +4061,8 @@ namespace DS4Windows
gyroAccelX = exposedState.getAccelX(); gyroAccelX = exposedState.getAccelX();
gyroAccelZ = exposedState.getAccelZ(); gyroAccelZ = exposedState.getAccelZ();
//gyroAccelX = exposedState.OutputAccelX;
//gyroAccelZ = exposedState.OutputAccelZ;
// If calibration values are missing then use "educated guesses" about good starting values // If calibration values are missing then use "educated guesses" about good starting values
if (controller.wheelCenterPoint.IsEmpty) if (controller.wheelCenterPoint.IsEmpty)
@ -4086,11 +4092,19 @@ namespace DS4Windows
} }
int maxRangeRight = Global.getSASteeringWheelEmulationRange(device) / 2 * C_WHEEL_ANGLE_PRECISION; int maxRangeRight = Global.GetSASteeringWheelEmulationRange(device) / 2 * C_WHEEL_ANGLE_PRECISION;
int maxRangeLeft = -maxRangeRight; int maxRangeLeft = -maxRangeRight;
result = CalculateControllerAngle(gyroAccelX, gyroAccelZ, controller); result = CalculateControllerAngle(gyroAccelX, gyroAccelZ, controller);
// Apply deadzone (SA X-deadzone value). This code assumes that 20deg is the max deadzone anyone ever might wanna use (in practice effective deadzone
// is probably just few degrees by using SXDeadZone values 0.01...0.05)
double sxDead = getSXDeadzone(device);
if ( sxDead > 0 && result != 0 && Math.Abs(result) < (20.0 * C_WHEEL_ANGLE_PRECISION * sxDead) )
{
result = 0;
}
// If wrapped around from +180 to -180 side (or vice versa) then SA steering wheel keeps on turning beyond 360 degrees (if range is >360) // If wrapped around from +180 to -180 side (or vice versa) then SA steering wheel keeps on turning beyond 360 degrees (if range is >360)
int wheelFullTurnCount = controller.wheelFullTurnCount; int wheelFullTurnCount = controller.wheelFullTurnCount;
if (controller.wheelPrevPhysicalAngle < 0 && result > 0) if (controller.wheelPrevPhysicalAngle < 0 && result > 0)
@ -4134,28 +4148,76 @@ namespace DS4Windows
result = Mapping.ClampInt(maxRangeLeft, result, maxRangeRight); result = Mapping.ClampInt(maxRangeLeft, result, maxRangeRight);
// Debug log output of SA sensor values // Debug log output of SA sensor values
//LogToGuiSACalibrationDebugMsg($"DEBUG gyro=({gyroAccelX}, {gyroAccelZ}) gyroPitchRollYaw=({currentDeviceState.Motion.gyroPitch}, {currentDeviceState.Motion.gyroRoll}, {currentDeviceState.Motion.gyroYaw}) gyroPitchRollYaw=({currentDeviceState.Motion.angVelPitch}, {currentDeviceState.Motion.angVelRoll}, {currentDeviceState.Motion.angVelYaw}) angle={result / (1.0 * C_WHEEL_ANGLE_PRECISION)} fullTurns={controller.wheelFullTurnCount}", false); //LogToGuiSACalibrationDebugMsg($"DBG gyro=({gyroAccelX}, {gyroAccelZ}) output=({exposedState.OutputAccelX}, {exposedState.OutputAccelZ}) PitRolYaw=({currentDeviceState.Motion.gyroPitch}, {currentDeviceState.Motion.gyroRoll}, {currentDeviceState.Motion.gyroYaw}) VelPitRolYaw=({currentDeviceState.Motion.angVelPitch}, {currentDeviceState.Motion.angVelRoll}, {currentDeviceState.Motion.angVelYaw}) angle={result / (1.0 * C_WHEEL_ANGLE_PRECISION)} fullTurns={controller.wheelFullTurnCount}", false);
// Scale input to a raw x360 16bit output scale, except if output axis of steering wheel emulation is L2+R2 trigger axis. // Apply anti-deadzone (SA X-antideadzone value)
switch(Global.getSASteeringWheelEmulationAxis(device)) double sxAntiDead = getSXAntiDeadzone(device);
switch (Global.GetSASteeringWheelEmulationAxis(device))
{ {
case SASteeringWheelEmulationAxisType.LX: case SASteeringWheelEmulationAxisType.LX:
case SASteeringWheelEmulationAxisType.LY: case SASteeringWheelEmulationAxisType.LY:
case SASteeringWheelEmulationAxisType.RX: case SASteeringWheelEmulationAxisType.RX:
case SASteeringWheelEmulationAxisType.RY: case SASteeringWheelEmulationAxisType.RY:
// DS4 thumbstick axis output (-32768..32767 raw value range) // DS4 thumbstick axis output (-32768..32767 raw value range)
//return (((result - maxRangeLeft) * (32767 - (-32768))) / (maxRangeRight - maxRangeLeft)) + (-32768);
if (result == 0) return 0;
if (sxAntiDead > 0)
{
sxAntiDead *= 32767;
if (result < 0) return (((result - maxRangeLeft) * (-Convert.ToInt32(sxAntiDead) - (-32768))) / (0 - maxRangeLeft)) + (-32768);
else return (((result - 0) * (32767 - (Convert.ToInt32(sxAntiDead)))) / (maxRangeRight - 0)) + (Convert.ToInt32(sxAntiDead));
}
else
{
return (((result - maxRangeLeft) * (32767 - (-32768))) / (maxRangeRight - maxRangeLeft)) + (-32768); return (((result - maxRangeLeft) * (32767 - (-32768))) / (maxRangeRight - maxRangeLeft)) + (-32768);
}
case SASteeringWheelEmulationAxisType.L2R2: case SASteeringWheelEmulationAxisType.L2R2:
// DS4 Trigger axis output. L2+R2 triggers share the same axis in x360 xInput/DInput controller, // DS4 Trigger axis output. L2+R2 triggers share the same axis in x360 xInput/DInput controller,
// so L2+R2 steering output supports only 360 turn range (-255..255 raw value range in the shared trigger axis) // so L2+R2 steering output supports only 360 turn range (-255..255 raw value range in the shared trigger axis)
// return (((result - (-180)) * (255 - (-255))) / (180 - (-180))) + (-255);
if (result == 0) return 0;
result = Convert.ToInt32(Math.Round(result / (1.0 * C_WHEEL_ANGLE_PRECISION))); result = Convert.ToInt32(Math.Round(result / (1.0 * C_WHEEL_ANGLE_PRECISION)));
if (result < 0) result = -181 - result; if (result < 0) result = -181 - result;
if (sxAntiDead > 0)
{
sxAntiDead *= 255;
if(result < 0) return (((result - (-180)) * (-Convert.ToInt32(sxAntiDead) - (-255))) / (0 - (-180))) + (-255);
else return (((result - (0)) * (255 - (Convert.ToInt32(sxAntiDead)))) / (180 - (0))) + (Convert.ToInt32(sxAntiDead));
}
else
{
return (((result - (-180)) * (255 - (-255))) / (180 - (-180))) + (-255); return (((result - (-180)) * (255 - (-255))) / (180 - (-180))) + (-255);
}
case SASteeringWheelEmulationAxisType.VJoy1X:
case SASteeringWheelEmulationAxisType.VJoy1Y:
case SASteeringWheelEmulationAxisType.VJoy1Z:
case SASteeringWheelEmulationAxisType.VJoy2X:
case SASteeringWheelEmulationAxisType.VJoy2Y:
case SASteeringWheelEmulationAxisType.VJoy2Z:
// SASteeringWheelEmulationAxisType.VJoy1X/VJoy1Y/VJoy1Z/VJoy2X/VJoy2Y/VJoy2Z VJoy axis output (0..32767 raw value range by default)
// return (((result - maxRangeLeft) * (32767 - (-0))) / (maxRangeRight - maxRangeLeft)) + (-0);
if (result == 0) return 16384;
if (sxAntiDead > 0)
{
sxAntiDead *= 16384;
if (result < 0) return (((result - maxRangeLeft) * (16384 - Convert.ToInt32(sxAntiDead) - (-0))) / (0 - maxRangeLeft)) + (-0);
else return (((result - 0) * (32767 - (16384 + Convert.ToInt32(sxAntiDead)))) / (maxRangeRight - 0)) + (16384 + Convert.ToInt32(sxAntiDead));
}
else
{
return (((result - maxRangeLeft) * (32767 - (-0))) / (maxRangeRight - maxRangeLeft)) + (-0);
}
default: default:
// SASteeringWheelEmulationAxisType.VJoy1X/VJoy1Y/VJoy1Z/VJoy2X/VJoy2Y/VJoy2Z VJoy axis output (0..32767 raw value range by default) // Should never come here, but C# case statement syntax requires DEFAULT handler
return (((result - maxRangeLeft) * (32767 - (-0))) / (maxRangeRight - maxRangeLeft)) + (-0); return 0;
} }
} }
} }

View File

@ -721,13 +721,13 @@ namespace DS4Windows
} }
public static SASteeringWheelEmulationAxisType[] SASteeringWheelEmulationAxis => m_Config.sASteeringWheelEmulationAxis; public static SASteeringWheelEmulationAxisType[] SASteeringWheelEmulationAxis => m_Config.sASteeringWheelEmulationAxis;
public static SASteeringWheelEmulationAxisType getSASteeringWheelEmulationAxis(int index) public static SASteeringWheelEmulationAxisType GetSASteeringWheelEmulationAxis(int index)
{ {
return m_Config.sASteeringWheelEmulationAxis[index]; return m_Config.sASteeringWheelEmulationAxis[index];
} }
public static int[] SASteeringWheelEmulationRange => m_Config.sASteeringWheelEmulationRange; public static int[] SASteeringWheelEmulationRange => m_Config.sASteeringWheelEmulationRange;
public static int getSASteeringWheelEmulationRange(int index) public static int GetSASteeringWheelEmulationRange(int index)
{ {
return m_Config.sASteeringWheelEmulationRange[index]; return m_Config.sASteeringWheelEmulationRange[index];
} }

View File

@ -129,7 +129,7 @@ namespace DS4Windows
if (state.PS) Output[11] |= (Byte)(1 << 2); // Guide if (state.PS) Output[11] |= (Byte)(1 << 2); // Guide
SASteeringWheelEmulationAxisType steeringWheelMappedAxis = Global.getSASteeringWheelEmulationAxis(device); SASteeringWheelEmulationAxisType steeringWheelMappedAxis = Global.GetSASteeringWheelEmulationAxis(device);
Int32 ThumbLX; Int32 ThumbLX;
Int32 ThumbLY; Int32 ThumbLY;
Int32 ThumbRX; Int32 ThumbRX;
@ -140,6 +140,13 @@ namespace DS4Windows
switch(steeringWheelMappedAxis) switch(steeringWheelMappedAxis)
{ {
case SASteeringWheelEmulationAxisType.None:
ThumbLX = Scale(state.LX, false);
ThumbLY = Scale(state.LY, true);
ThumbRX = Scale(state.RX, false);
ThumbRY = Scale(state.RY, true);
break;
case SASteeringWheelEmulationAxisType.LX: case SASteeringWheelEmulationAxisType.LX:
ThumbLX = state.SASteeringWheelEmulationUnit; ThumbLX = state.SASteeringWheelEmulationUnit;
ThumbLY = Scale(state.LY, true); ThumbLY = Scale(state.LY, true);
@ -172,29 +179,26 @@ namespace DS4Windows
Output[12] = Output[13] = 0; Output[12] = Output[13] = 0;
if (state.SASteeringWheelEmulationUnit >= 0) Output[12] = (Byte)state.SASteeringWheelEmulationUnit; if (state.SASteeringWheelEmulationUnit >= 0) Output[12] = (Byte)state.SASteeringWheelEmulationUnit;
else Output[13] = (Byte)state.SASteeringWheelEmulationUnit; else Output[13] = (Byte)state.SASteeringWheelEmulationUnit;
goto default; // Usually GOTO is not a good idea but in switch-case statements it is sometimes pretty handy and acceptable way to fall through case options goto case SASteeringWheelEmulationAxisType.None;
case SASteeringWheelEmulationAxisType.VJoy1X: case SASteeringWheelEmulationAxisType.VJoy1X:
case SASteeringWheelEmulationAxisType.VJoy2X: case SASteeringWheelEmulationAxisType.VJoy2X:
DS4Windows.VJoyFeeder.vJoyFeeder.FeedAxisValue(state.SASteeringWheelEmulationUnit, ((((uint)steeringWheelMappedAxis) - ((uint)SASteeringWheelEmulationAxisType.VJoy1X)) / 3) + 1, DS4Windows.VJoyFeeder.HID_USAGES.HID_USAGE_X); DS4Windows.VJoyFeeder.vJoyFeeder.FeedAxisValue(state.SASteeringWheelEmulationUnit, ((((uint)steeringWheelMappedAxis) - ((uint)SASteeringWheelEmulationAxisType.VJoy1X)) / 3) + 1, DS4Windows.VJoyFeeder.HID_USAGES.HID_USAGE_X);
goto default; goto case SASteeringWheelEmulationAxisType.None;
case SASteeringWheelEmulationAxisType.VJoy1Y: case SASteeringWheelEmulationAxisType.VJoy1Y:
case SASteeringWheelEmulationAxisType.VJoy2Y: case SASteeringWheelEmulationAxisType.VJoy2Y:
DS4Windows.VJoyFeeder.vJoyFeeder.FeedAxisValue(state.SASteeringWheelEmulationUnit, ((((uint)steeringWheelMappedAxis) - ((uint)SASteeringWheelEmulationAxisType.VJoy1X)) / 3) + 1, DS4Windows.VJoyFeeder.HID_USAGES.HID_USAGE_Y); DS4Windows.VJoyFeeder.vJoyFeeder.FeedAxisValue(state.SASteeringWheelEmulationUnit, ((((uint)steeringWheelMappedAxis) - ((uint)SASteeringWheelEmulationAxisType.VJoy1X)) / 3) + 1, DS4Windows.VJoyFeeder.HID_USAGES.HID_USAGE_Y);
goto default; goto case SASteeringWheelEmulationAxisType.None;
case SASteeringWheelEmulationAxisType.VJoy1Z: case SASteeringWheelEmulationAxisType.VJoy1Z:
case SASteeringWheelEmulationAxisType.VJoy2Z: case SASteeringWheelEmulationAxisType.VJoy2Z:
DS4Windows.VJoyFeeder.vJoyFeeder.FeedAxisValue(state.SASteeringWheelEmulationUnit, ((((uint)steeringWheelMappedAxis) - ((uint)SASteeringWheelEmulationAxisType.VJoy1X)) / 3) + 1, DS4Windows.VJoyFeeder.HID_USAGES.HID_USAGE_Z); DS4Windows.VJoyFeeder.vJoyFeeder.FeedAxisValue(state.SASteeringWheelEmulationUnit, ((((uint)steeringWheelMappedAxis) - ((uint)SASteeringWheelEmulationAxisType.VJoy1X)) / 3) + 1, DS4Windows.VJoyFeeder.HID_USAGES.HID_USAGE_Z);
goto default; goto case SASteeringWheelEmulationAxisType.None;
default: default:
ThumbLX = Scale(state.LX, false); // Should never come here but just in case use the NONE case as default handler....
ThumbLY = Scale(state.LY, true); goto case SASteeringWheelEmulationAxisType.None;
ThumbRX = Scale(state.RX, false);
ThumbRY = Scale(state.RY, true);
break;
} }
Output[14] = (Byte)((ThumbLX >> 0) & 0xFF); // LX Output[14] = (Byte)((ThumbLX >> 0) & 0xFF); // LX

View File

@ -719,9 +719,9 @@ namespace DS4Windows
cBGyroMouseXAxis.SelectedIndex = GyroMouseHorizontalAxis[device]; cBGyroMouseXAxis.SelectedIndex = GyroMouseHorizontalAxis[device];
triggerCondAndCombo.SelectedIndex = SATriggerCond[device] ? 0 : 1; triggerCondAndCombo.SelectedIndex = SATriggerCond[device] ? 0 : 1;
cBSteeringWheelEmulationAxis.SelectedIndex = (int) getSASteeringWheelEmulationAxis(device); cBSteeringWheelEmulationAxis.SelectedIndex = (int) GetSASteeringWheelEmulationAxis(device);
int idxSASteeringWheelEmulationRange = cBSteeringWheelEmulationRange.Items.IndexOf(getSASteeringWheelEmulationRange(device).ToString()); int idxSASteeringWheelEmulationRange = cBSteeringWheelEmulationRange.Items.IndexOf(GetSASteeringWheelEmulationRange(device).ToString());
if (idxSASteeringWheelEmulationRange >= 0) cBSteeringWheelEmulationRange.SelectedIndex = idxSASteeringWheelEmulationRange; if (idxSASteeringWheelEmulationRange >= 0) cBSteeringWheelEmulationRange.SelectedIndex = idxSASteeringWheelEmulationRange;
} }
else else

View File

@ -4,7 +4,7 @@
// https://github.com/shauleiz/vJoy/tree/master/apps/common/vJoyInterfaceCS // https://github.com/shauleiz/vJoy/tree/master/apps/common/vJoyInterfaceCS
// //
// This module is a feeder for VJoy virtual joystick driver. DS4Windows can optionally re-map and feed buttons and analog axis values from DS4 Controller to VJoy device. // This module is a feeder for VJoy virtual joystick driver. DS4Windows can optionally re-map and feed buttons and analog axis values from DS4 Controller to VJoy device.
// At first this may seem silly because DS4Windows can already to re-mapping by using a virtual X360 Controller driver, so why feed VJoy virtual driver also? // At first this may seem silly because DS4Windows can already do re-mapping by using a virtual X360 Controller driver, so why feed VJoy virtual driver also?
// Sometimes X360 driver may run out of analog axis options, so for example "SA motion sensor steering wheel emulation" in DS4Windows would reserve a thumbstick X or Y // Sometimes X360 driver may run out of analog axis options, so for example "SA motion sensor steering wheel emulation" in DS4Windows would reserve a thumbstick X or Y
// axis for SA steering wheel emulation usage. That thumbstick axis would be unavailable for "normal" thumbstick usage after this re-mapping. // axis for SA steering wheel emulation usage. That thumbstick axis would be unavailable for "normal" thumbstick usage after this re-mapping.
// The problem can be solved by configuring DS4Windows to re-map SA steering wheel emulation axis to VJoy axis, so all analog axies in DS4 controller are still available for normal usage. // The problem can be solved by configuring DS4Windows to re-map SA steering wheel emulation axis to VJoy axis, so all analog axies in DS4 controller are still available for normal usage.
@ -12,6 +12,7 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Security; // SuppressUnmanagedCodeSecurity support to optimize for performance instead of code security
namespace DS4Windows.VJoyFeeder namespace DS4Windows.VJoyFeeder
{ {
@ -105,6 +106,7 @@ namespace DS4Windows.VJoyFeeder
//namespace vJoyInterfaceWrap //namespace vJoyInterfaceWrap
//{ //{
[SuppressUnmanagedCodeSecurity]
public class vJoy public class vJoy
{ {
@ -698,12 +700,17 @@ namespace DS4Windows.VJoyFeeder
// Feed axis value to VJoy virtual joystic driver (DS4Windows sixaxis (SA) motion sensor steering wheel emulation feature can optionally feed VJoy analog axis instead of ScpVBus x360 axis // Feed axis value to VJoy virtual joystic driver (DS4Windows sixaxis (SA) motion sensor steering wheel emulation feature can optionally feed VJoy analog axis instead of ScpVBus x360 axis
public static void FeedAxisValue(int value, uint vJoyID, HID_USAGES axis) public static void FeedAxisValue(int value, uint vJoyID, HID_USAGES axis)
{ {
if (!vJoyInitialized)
InitializeVJoyDevice(vJoyID, axis);
if (vJoyAvailable) if (vJoyAvailable)
{
vJoyObj.SetAxis(value, vJoyID, axis); vJoyObj.SetAxis(value, vJoyID, axis);
} }
else if (!vJoyInitialized)
{
// If this was the first call to this FeedAxisValue function and VJoy driver connection is not yet initialized
// then try to do it now. Subsequent calls will see the the vJoy as available (if connection succeeded) and
// there is no need to re-initialize the connection everytime the feeder is used.
InitializeVJoyDevice(vJoyID, axis);
}
}
} }
} }