apply non-linear steering curve
This commit is contained in:
@@ -39,6 +39,13 @@ static int mode = G29_MODE_MEDIA;
|
|||||||
module_param(mode, int, 0444);
|
module_param(mode, int, 0444);
|
||||||
MODULE_PARM_DESC(mode, "Mapping mode (0=MEDIA)");
|
MODULE_PARM_DESC(mode, "Mapping mode (0=MEDIA)");
|
||||||
|
|
||||||
|
/* Steering curve exponent (100 = linear, 200 = squared, 150 = ^1.5)
|
||||||
|
* Higher values reduce sensitivity at low steering angles.
|
||||||
|
*/
|
||||||
|
static int steer_curve = 200;
|
||||||
|
module_param(steer_curve, int, 0644);
|
||||||
|
MODULE_PARM_DESC(steer_curve, "Steering sensitivity curve (100=linear, 200=squared, default=200)");
|
||||||
|
|
||||||
struct g29_keymap_edge {
|
struct g29_keymap_edge {
|
||||||
u32 mask;
|
u32 mask;
|
||||||
unsigned short keycode;
|
unsigned short keycode;
|
||||||
@@ -85,21 +92,59 @@ static void g29_steer_timer_fn(struct timer_list *t) {
|
|||||||
struct g29_dev *g29 = timer_container_of(g29, t, steer_timer);
|
struct g29_dev *g29 = timer_container_of(g29, t, steer_timer);
|
||||||
|
|
||||||
const int rot = le16_to_cpu(g29->last.rot_le);
|
const int rot = le16_to_cpu(g29->last.rot_le);
|
||||||
int distance_from_center = abs(rot - WHEEL_CENTER);
|
int distance_to_center = abs(rot - WHEEL_CENTER);
|
||||||
bool press_key;
|
int adjusted_distance;
|
||||||
|
|
||||||
|
//pr_info("g29: rot=%d distance_to_center=%d\n", rot, distance_to_center);
|
||||||
|
|
||||||
|
/* Apply non-linear steering curve:
|
||||||
|
* adjusted = (distance / MAX)^(curve/100) * MAX
|
||||||
|
*
|
||||||
|
* For curve=200 (squared): 5% input -> 0.25% output, 50% -> 25%, 100% -> 100%
|
||||||
|
* For curve=150 (^1.5): 5% input -> ~1.1% output, 50% -> ~35%, 100% -> 100%
|
||||||
|
* For curve=100 (linear): 5% input -> 5% output (no change)
|
||||||
|
*
|
||||||
|
* Using integer math: adjusted = (distance^2 / MAX) for curve=200
|
||||||
|
*/
|
||||||
|
if (steer_curve == 100) {
|
||||||
|
/* Linear response - no adjustment */
|
||||||
|
adjusted_distance = distance_to_center;
|
||||||
|
} else if (steer_curve == 200) {
|
||||||
|
/* Squared response - optimized integer calculation */
|
||||||
|
adjusted_distance = (distance_to_center * distance_to_center) / WHEEL_MAX_DIST;
|
||||||
|
} else {
|
||||||
|
/* Generic power curve using normalized values (0-1000 range for precision)
|
||||||
|
* normalized = (distance * 1000) / WHEEL_MAX_DIST
|
||||||
|
* Apply power approximation, then scale back
|
||||||
|
*/
|
||||||
|
int normalized = (distance_to_center * 1000) / WHEEL_MAX_DIST;
|
||||||
|
int powered;
|
||||||
|
|
||||||
|
if (steer_curve == 150) {
|
||||||
|
/* Approximate ^1.5 with (x * sqrt(x)) */
|
||||||
|
int sqrt_norm = int_sqrt(normalized * 1000);
|
||||||
|
powered = (normalized * sqrt_norm) / 1000;
|
||||||
|
} else {
|
||||||
|
/* Fallback: squared for any other value > 100 */
|
||||||
|
powered = (normalized * normalized) / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
adjusted_distance = (powered * WHEEL_MAX_DIST) / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
/* Phase accumulator approach:
|
/* Phase accumulator approach:
|
||||||
* Accumulate the distance on each tick.
|
* Accumulate the adjusted distance on each tick.
|
||||||
* When it exceeds the max distance, press the key and wrap.
|
* When it exceeds the max distance, press the key and wrap.
|
||||||
* This gives us a duty cycle of (distance / WHEEL_MAX_DIST).
|
* This gives us a duty cycle of (adjusted_distance / WHEEL_MAX_DIST).
|
||||||
*
|
*
|
||||||
* Examples:
|
* Examples (with curve=200):
|
||||||
* distance = WHEEL_MAX_DIST/2 (50%) -> press every 2nd tick
|
* 5% steering -> ~0.25% press rate
|
||||||
* distance = WHEEL_MAX_DIST (100%) -> press every tick
|
* 50% steering -> 25% press rate
|
||||||
* distance = WHEEL_MAX_DIST/4 (25%) -> press every 4th tick
|
* 100% steering -> 100% press rate (every tick)
|
||||||
*/
|
*/
|
||||||
g29->phase_accumulator += distance_from_center;
|
g29->phase_accumulator += adjusted_distance;
|
||||||
|
|
||||||
|
bool press_key;
|
||||||
if (g29->phase_accumulator >= WHEEL_MAX_DIST) {
|
if (g29->phase_accumulator >= WHEEL_MAX_DIST) {
|
||||||
g29->phase_accumulator -= WHEEL_MAX_DIST;
|
g29->phase_accumulator -= WHEEL_MAX_DIST;
|
||||||
press_key = true;
|
press_key = true;
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
#define G29_BTN_RED_CW 0x02000000u
|
#define G29_BTN_RED_CW 0x02000000u
|
||||||
#define G29_BTN_RED_CCW 0x04000000u
|
#define G29_BTN_RED_CCW 0x04000000u
|
||||||
#define G29_BTN_RETURN 0x08000000u
|
#define G29_BTN_RETURN 0x08000000u
|
||||||
|
#define G29_BTN_PS3_LOGO 0xF0000000u
|
||||||
|
|
||||||
#define G29_DPAD_MASK 0x0000000Eu
|
#define G29_DPAD_MASK 0x0000000Eu
|
||||||
#define G29_DPAD_UP 0x00000000u
|
#define G29_DPAD_UP 0x00000000u
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ Logitech G29 USB Protocol
|
|||||||
- `0x02000000` - Red rotation clockwise
|
- `0x02000000` - Red rotation clockwise
|
||||||
- `0x04000000` - Red rotation counterclockwise
|
- `0x04000000` - Red rotation counterclockwise
|
||||||
- `0x08000000` - Return
|
- `0x08000000` - Return
|
||||||
- `0xF0000000` - ?
|
- `0xF0000000` - Playstation 3 Logo Button (verify)
|
||||||
- `Rot`: Wheel rotation (little-endian). `0x0000` (leftmost) - `0xFFFF` (rightmost).
|
- `Rot`: Wheel rotation (little-endian). `0x0000` (leftmost) - `0xFFFF` (rightmost).
|
||||||
- `Gas`: Gas pedal. `0xFF` (up, default) - `0x00` (down).
|
- `Gas`: Gas pedal. `0xFF` (up, default) - `0x00` (down).
|
||||||
- `Brk`: Brake pedal. `0xFF` (up, default) - `0x00` (down).
|
- `Brk`: Brake pedal. `0xFF` (up, default) - `0x00` (down).
|
||||||
|
|||||||
Reference in New Issue
Block a user