Commit Graph

4 Commits

Author SHA1 Message Date
1e8c294a80 forza-trigger: gate throttle on clutch state
User report: with the clutch in (pedal pressed, engine disconnected from
wheels), steering left still produced resistance on R2. The throttle
shouldn't have any feel when it's mechanically irrelevant.

RacingDSX's throttle resistance formula is
`avgAccel = sqrt(0.25*X^2 + 1.0*Z^2)`
derived from the accelerometer alone. It never checks clutch state, so
cornering G-forces keep producing trigger resistance even while the
clutch pedal is floored. Bug.

Fix: when Forza's clutch byte > 128 (clutch fully or mostly disengaged)
bypass the entire throttle path \u2014 slip detection and non-slip Feedback
both \u2014 and release the trigger. Uses the same one-shot 0x05 (active
retract) on transition + steady-state 0x00 (no-op) pattern as the
in-race \u2192 not-in-race transition (divergence #4) so we don't get the
trigger-motor whine from re-asserting 0x05 every frame.

Brake is unaffected: brake calipers operate independently of clutch
state, so ABS feel during clutch-in is still correct.

For auto-clutch users this also produces brief (~100 ms) trigger
relaxations during shifts \u2014 physically accurate (the engine *is*
momentarily disconnected during a shift) and matches the haptic feel of
a real manual transmission.

Documented as divergence #5 in the module docstring.
2026-05-03 00:35:49 -04:00
b25cb4a90f forza-trigger: stop emitting mode 0x05 every frame in pre-race idle
The previous fix used canonical Off (mode 0x05) everywhere we wanted the
trigger to feel released \u2014 pre-race per-frame, idle timeout, shutdown.
Per Sony's docs (Nielk1 Rev 6) mode 0x05 "actively returns the trigger
stop to the neutral position". Re-asserting it 60 times/sec from main
thread, propagated by pydualsense's BG thread to the controller at
~250 Hz, made the trigger motor audibly whine as the firmware repeatedly
snapped the (already-neutral) trigger back to neutral.

Right answer: hybrid. One-shot 0x05 on the in-race \u2192 not-in-race
transition (and on the telemetry-idle timeout) so the firmware actually
retracts the motor; mode 0x00 (TriggerModes.Off, no-op clear) for
steady-state pre-race / idle frames so we're not yelling RESET in the
firmware's ear forever.

Implementation: prev_in_race tracks the last frame's race state. Steady
non-race frames call _apply_normal (mode 0x00); the first frame after a
race-end transition calls _apply_off (mode 0x05). pydualsense's BG
thread holds the 0x05 in memory long enough (one main-thread frame =
~16ms = ~4 BG iterations) to publish it to the controller before main
switches the in-memory state to 0x00.

Restores _apply_normal and DS_MODE_NORMAL that the previous commit
deleted. Updates divergence #4 in the module docstring.
2026-05-03 00:35:49 -04:00
876864c854 forza-trigger: actively release trigger and clear lightbar on idle
Two issues in the deployed daemon:

  1. After FH5 exits, the lightbar stayed lit. reset_triggers() touched
     only triggers; pydualsense's BG sendReport thread kept re-publishing
     whatever TouchpadColor we last set, so the controller stayed in the
     last race color forever.

  2. R2 had residual tension in FH5's main menu and on the desktop after
     a race. Pre-race / idle states were emitting RacingDSX's NormalTrigger
     (mode byte 0x00), which per Sony's docs (Nielk1 Rev6) only clears
     state without retracting the trigger motor; mode 0x05 (canonical Off
     / Reset) actively returns the trigger to neutral. RacingDSX-on-Windows
     gets away with 0x00 because something else (Steam Input or the OS)
     reliably resets the motor on focus loss; on Linux nothing does.

Fixes:
  - Drop _apply_normal/DS_MODE_NORMAL. Use _apply_off (mode 0x05) for every
    'release the trigger' intent: pre-race per-frame, idle timeout, mid-race
    zero-strength fallback, shutdown.
  - Add reset_lightbar() that writes RGB(0,0,0).
  - Track have_telemetry and fire the idle-timeout branch whenever
    telemetry has been silent for IDLE_TIMEOUT_S, regardless of in_race.
    Reset both triggers and lightbar in that branch.

Documented as divergence #4 in the module docstring.
2026-05-03 00:35:49 -04:00
31c309af1f yarn: forza dualsense adaptive trigger bridge 2026-05-03 00:35:48 -04:00